/*
 * $Id: bfd.c,v 1.37.2.45 Broadcom SDK $
 *
 * $Copyright: Copyright 2011 Broadcom Corporation.
 * This program is the proprietary software of Broadcom Corporation
 * and/or its licensors, and may only be used, duplicated, modified
 * or distributed pursuant to the terms and conditions of a separate,
 * written license agreement executed between you and Broadcom
 * (an "Authorized License").  Except as set forth in an Authorized
 * License, Broadcom grants no license (express or implied), right
 * to use, or waiver of any kind with respect to the Software, and
 * Broadcom expressly reserves all rights in and to the Software
 * and all intellectual property rights therein.  IF YOU HAVE
 * NO AUTHORIZED LICENSE, THEN YOU HAVE NO RIGHT TO USE THIS SOFTWARE
 * IN ANY WAY, AND SHOULD IMMEDIATELY NOTIFY BROADCOM AND DISCONTINUE
 * ALL USE OF THE SOFTWARE.  
 *  
 * Except as expressly set forth in the Authorized License,
 *  
 * 1.     This program, including its structure, sequence and organization,
 * constitutes the valuable trade secrets of Broadcom, and you shall use
 * all reasonable efforts to protect the confidentiality thereof,
 * and to use this information only in connection with your use of
 * Broadcom integrated circuit products.
 *  
 * 2.     TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS
 * PROVIDED "AS IS" AND WITH ALL FAULTS AND BROADCOM MAKES NO PROMISES,
 * REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY,
 * OR OTHERWISE, WITH RESPECT TO THE SOFTWARE.  BROADCOM SPECIFICALLY
 * DISCLAIMS ANY AND ALL IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY,
 * NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES,
 * ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
 * CORRESPONDENCE TO DESCRIPTION. YOU ASSUME THE ENTIRE RISK ARISING
 * OUT OF USE OR PERFORMANCE OF THE SOFTWARE.
 * 
 * 3.     TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL
 * BROADCOM OR ITS LICENSORS BE LIABLE FOR (i) CONSEQUENTIAL,
 * INCIDENTAL, SPECIAL, INDIRECT, OR EXEMPLARY DAMAGES WHATSOEVER
 * ARISING OUT OF OR IN ANY WAY RELATING TO YOUR USE OF OR INABILITY
 * TO USE THE SOFTWARE EVEN IF BROADCOM HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES; OR (ii) ANY AMOUNT IN EXCESS OF
 * THE AMOUNT ACTUALLY PAID FOR THE SOFTWARE ITSELF OR USD 1.00,
 * WHICHEVER IS GREATER. THESE LIMITATIONS SHALL APPLY NOTWITHSTANDING
 * ANY FAILURE OF ESSENTIAL PURPOSE OF ANY LIMITED REMEDY.$
 */

#if defined (INCLUDE_L3)

#include <sal/core/libc.h>

#include <soc/defs.h>
#include <soc/drv.h>
#include <soc/mem.h>
#include <soc/profile_mem.h>
#include <soc/debug.h>
#include <soc/hash.h>
#include <soc/l2x.h>
#include <soc/l3x.h>
#include <soc/katana.h>
#include <bcm/bfd.h>

#define BCMSIM_DUMP_ENCAP 1
#undef BCMSIM_DUMP_ENCAP

#if defined(BCM_CMICM_SUPPORT)


#define BFD_UC_MESSAGE_ON       1
#undef BFD_UC_MESSAGE_ON

#include <soc/uc_msg.h>
#ifdef BFD_UC_MESSAGE_ON
#include <soc/shared/mos_msg_common.h>
#endif

#include <soc/shared/bfd.h>
#endif /* BCM_CMICM_SUPPORT */

#include <bcm_int/esw/bfd.h>
#include <bcm_int/esw/l3.h>
#include <bcm_int/esw/port.h>
#include <bcm_int/esw/switch.h>
#include <bcm_int/esw/katana.h>
#include <bcm_int/esw/virtual.h>
#include <bcm_int/esw_dispatch.h>

#if defined(BCM_TRIUMPH_SUPPORT)
#include <bcm_int/esw/triumph.h>
#endif /* BCM_TRIUMPH_SUPPORT */

#if defined(BCM_KATANA_SUPPORT)

#define CHECK_INIT \
    if (!bfd_info_p->initialized) \
    { \
        return BCM_E_INIT; \
    }

#define SET_BFD_INFO bfd_info_p = &kt_bfd_info[unit];
#define SET_BFD_MSG_REQ bfd_req_p = (bfd_ctrl_msg_t*)kt_bfd_info[unit].dmabuf_req;
#define SET_BFD_MSG_REPLY bfd_reply_p = (bfd_ctrl_msg_t*)kt_bfd_info[unit].dmabuf_reply;

#define SET_ENDPOINT(_endpoint_index_) endpoint_p = \
    bfd_info_p->endpoints + _endpoint_index_;

#define VALIDATE_ENDPOINT_INDEX(_endpoint_index_) \
    if ((_endpoint_index_) < 0 || \
        (_endpoint_index_) >= bfd_info_p->endpoint_count) \
    { \
        return BCM_E_PARAM; \
    } \

#define _BFD_NUM_CMICM          2

#define BFD_SHR_RES_ENCAP_TYPE 0
#define BFD_SHR_RES_TYPE_COUNT 1

#define BFD_SHR_RES_ENCAP_POOL 0
#define BFD_SHR_RES_POOL_COUNT 1

static char *bfd_msg_encap_pool_name = "bfd_msg_encap_pool";
static char *bfd_msg_encap_type_name = "bfd_msg_encap_type";

#define _BFD_UDP_SRCPORT_MIN                49152
#define _BFD_UDP_SRCPORT_MAX                65535
#define VALIDATE_UDP_SRCPORT(p) \
    if ( ((p) < _BFD_UDP_SRCPORT_MIN) || ((p) > _BFD_UDP_SRCPORT_MAX) ) \
    { \
        return BCM_E_PARAM; \
    } \

#define VALIDATE_IPV4_LOOPBACK(a) \
    if ( 127 != ( ((a) >> 24) & 0xff) ) { \
        return BCM_E_PARAM; \
    } \

#define VALIDATE_IPV4_ROUTABLE(a) \
    if ( (10 == ( ((a) >> 24) & 0xff)) ) {             /* Class A reserved */ \
        return BCM_E_PARAM; \
    }else if ( (172 == ( ((a) >> 24) & 0xff)) &&       /* Class B reserved */ \
               (16 == ( ((a) >> 16) & 0xf0)) ) { \
        return BCM_E_PARAM; \
    }else if ( (192 == ( ((a) >> 24) & 0xff)) &&       /* Class C reserved */ \
               (168 == ( ((a) >> 16) & 0xff)) ) { \
        return BCM_E_PARAM; \
    }else if ( (240 == ( ((a) >> 24) & 0xff)) ) {      /* Class E research */ \
        return BCM_E_PARAM; \
    } \


#define _BFD_MPLS_ETYPE                     0x8847
#define _BFD_IPV4_ETYPE                     0x0800
#define _BFD_IPV4_VERSION_LENGTH            0x45
#define _BFD_IPV4_UDP_PROTOCOL              17
#define _BFD_IPV4_HEADER_LENGTH             20
#define _BFD_IPV6_ETYPE                     0x86DD
#define _BFD_IPV6_VERSION                   6
#define _BFD_IPV6_HEADER_LENGTH             40
#define _BFD_ACH_HEADER_LENGTH              4
#define _BFD_MPLS_HEADER_LENGTH             4
#define _BFD_UDP_HEADER_LENGTH              8
#define _BFD_BASE_HEADER_LENGTH             24
#define _BFD_AUTH_SP_HEADER_START_LENGTH    3
#define _BFD_AUTH_SHA1_LENGTH               28
#define _BFD_L2_HEADER_LENGTH               14
#define _BFD_VLAN_HEADER_LENGTH             4
#define _BFD_UDP_SINGLE_HOP_DEST_PORT       3784
#define _BFD_UDP_MULTI_HOP_DEST_PORT        4784
#define _BFD_MPLS_MAX_LABELS                3
#define _BFD_MPLS_ROUTER_ALERT_LABEL        1
#define _BFD_MPLS_GAL_LABEL                 13

#define _BFD_ACH_CHANNEL_TYPE_RAW           0x0007
#define _BFD_ACH_CHANNEL_TYPE_RAW_CCCV      0x0008
#define _BFD_ACH_PWACH_VERSION              0x10
#define _BFD_ACH_CHANNEL_TYPE_IPV4          0x0021
#define _BFD_ACH_CHANNEL_TYPE_IPV6          0x0057

#define _BFD_UC_MSG_TIMEOUT_USECS           1000000

typedef struct
{
    bcm_mac_t dst_mac;
    bcm_mac_t src_mac;
    uint16 etype;
}_l2_hdr_t;

typedef struct
{
    uint8         v_hl;
    uint8         tos;
    uint16        len;
    uint16        id;
    uint16        off;
    uint8         ttl;
    uint8         p;
    uint16        sum;
    uint32        src;
    uint32        dst;
} _ipv4_hdr_t;

typedef struct
{
    uint32          version : 4;
    uint32          tclass : 8;
    uint32          flable : 20;
    uint16          plength;
    uint8           nexthdr;
    uint8           hoplimit;
    bcm_ip6_t       src;
    bcm_ip6_t       dst;
} _ipv6_hdr_t;

typedef struct
{
    uint16            src;
    uint16            dst;
    uint16            len;
    uint16            cksum;
} _udp_hdr_t;

static _bcm_bfd_info_t kt_bfd_info[BCM_MAX_NUM_UNITS];

static void *_bcm_kt_bfd_alloc_clear(unsigned int size, char *description)
{
    void *block_p = 0;

    if (size)
    {
        block_p = sal_alloc(size, description);

        if (block_p != NULL)
        {
            sal_memset(block_p, 0, size);
        }
    }

    return block_p;
}

#ifdef BCM_WARM_BOOT_SUPPORT

#define BCM_WB_VERSION_1_0                SOC_SCACHE_VERSION(1,0)
#define BCM_WB_DEFAULT_VERSION            BCM_WB_VERSION_1_0

static int _bcm_kt_bfd_warm_boot(int unit)
{

#if 0
    /* Do this get for each possible session for WARMBOOT */
    get_p = &((bfd_msg_ctrl_t*)bfd_info_p->dmabuf)->u.get;
    sal_memset(get_p, 0, sizeof(bfd_msg_ctrl_sess_get_t));
    soc_cm_sflush(unit, get_p, sizeof(bfd_msg_ctrl_sess_get_t));

    sal_memset(&uc_msg, 0, sizeof(uc_msg));
    uc_msg.s.mclass = MOS_MSG_CLASS_BFD;
    uc_msg.s.subclass = MOS_MSG_SUBCLASS_BFD_SESS_GET;
    uc_msg.s.len = bcm_htons(endpoint_p->endpoint_index);
    uc_msg.s.data = bcm_htonl(soc_cm_l2p(unit, get_p));

    sal_memset(&uc_reply, 0, sizeof(uc_reply));

#ifdef BFD_UC_MESSAGE_ON
    result = soc_cmic_uc_msg_send_receive(unit, bfd_info_p->bte_num, &uc_msg, &uc_reply, _BFD_UC_MSG_TIMEOUT_USECS);
    if ( (SOC_E_NONE == result) &&
         (uc_reply.s.subclass == MOS_MSG_SUBCLASS_BFD_SESS_GET_REPLY) &&
         (uc_reply.s.mclass == MOS_MSG_CLASS_BFD) ) {
        
    }else{
        result = BCM_E_INTERNAL;
    }
#endif

    /* recover each entry of SHA1 authentication table for RAM cache */
    /* recover each entry of simple password authenticaton table for RAM cache
     */
#endif
    return BCM_E_NONE;
}

#endif /* BCM_WARM_BOOT_SUPPORT */


static void _bcm_kt_bfd_free_memory(int unit, _bcm_bfd_info_t *bfd_info_p)
{
    if (bfd_info_p->endpoints)
    {
        sal_free(bfd_info_p->endpoints);
    }
    if (bfd_info_p->endpoints_in_use)
    {
        sal_free(bfd_info_p->endpoints_in_use);
    }
    if (bfd_info_p->auth_sha1) 
    {
        sal_free(bfd_info_p->auth_sha1);
    }
    if (bfd_info_p->auth_sp) 
    {
        sal_free(bfd_info_p->auth_sp);
    }
    if (bfd_info_p->dmabuf_req)
    {
        soc_cm_sfree(unit, bfd_info_p->dmabuf_req);
    }
    if (bfd_info_p->dmabuf_reply)
    {
        soc_cm_sfree(unit, bfd_info_p->dmabuf_reply);
    }
    shr_mres_destroy(bfd_info_p->res_mgr);


}

static void _bcm_kt_bfd_event_unregister_all(_bcm_bfd_info_t *bfd_info_p)
{
    _bcm_bfd_event_handler_t *event_handler_p;
    _bcm_bfd_event_handler_t *event_handler_to_delete_p;

    event_handler_p = bfd_info_p->event_handler_list_p;

    while (event_handler_p != NULL)
    {
        event_handler_to_delete_p = event_handler_p;
        event_handler_p = event_handler_p->next_p;

        sal_free(event_handler_to_delete_p);
    }
    bfd_info_p->event_handler_list_p = NULL;
}

/*
 * Function:
 *      _bcm_kt_bfd
 * Purpose:
 *      Initialize the BFD subsystem
 * Parameters:
 *      unit - (IN) Unit number.
 * Returns:
 *      BCM_E_NONE Operation completed successfully
 *      BCM_E_MEMORY Unable to allocate memory for internal control structures
 *      BCM_E_INTERNAL Failed to initialize
 * Notes:
 */
STATIC void
_bcm_kt_bfd_callback_thread(void *param) {
    _bcm_bfd_info_t *bfd_info_p = param;
    _bcm_bfd_event_handler_t *event_handler_p;
    bcm_bfd_event_types_t events, cb_events;
    mos_msg_data_t rcv;
    bfd_msg_event_t *bfd_rcv;
    int i, invoke = 0;

    

    while (1) {
        /* wait on notifications from BTE */
        soc_cmic_uc_msg_receive(bfd_info_p->unit, bfd_info_p->bte_num, MOS_MSG_CLASS_BFD_EVENT, &rcv, sal_sem_FOREVER);

        bfd_rcv = (bfd_msg_event_t*)&rcv;

        sal_memset(&cb_events, 0, sizeof(bcm_bfd_event_types_t));
        sal_memset(&events, 0, sizeof(bcm_bfd_event_types_t));
        for (i=0; i < bcmBFDEventCount; i++) {
            if (bfd_rcv->s.events & i) {
                SHR_BITSET(events.w, i);
            }
        }

        /* loop over registered callbacks,
         * if any match the events field, then invoke
         */
        for (event_handler_p = bfd_info_p->event_handler_list_p;
            event_handler_p != NULL;
            event_handler_p = event_handler_p->next_p)
        {
            SHR_BITAND_RANGE(event_handler_p->event_types.w, events.w, 0, bcmBFDEventCount, cb_events.w);
            SHR_BITTEST_RANGE(cb_events.w, 0, bcmBFDEventCount, invoke);
            if (invoke)
            {
                
                event_handler_p->cb(bfd_info_p->unit, 0,
                    cb_events,
                    bfd_rcv->s.sess_id,
                    event_handler_p->user_data);
            }
        }
    }

    sal_thread_exit(0);

}

/*
 * Function:
 *      bcm_kt_bfd_init
 * Purpose:
 *      Initialize the BFD subsystem
 * Parameters:
 *      unit - (IN) Unit number.
 * Returns:
 *      BCM_E_NONE Operation completed successfully
 *      BCM_E_MEMORY Unable to allocate memory for internal control structures
 *      BCM_E_INTERNAL Failed to initialize
 * Notes:
 */
int
bcm_kt_bfd_init(
    int unit)
{
    _bcm_bfd_info_t *bfd_info_p;
    uint32 val;
    int result = BCM_E_NONE;
    bfd_msg_ctrl_init_t *init_p;
#ifdef BFD_UC_MESSAGE_ON
    mos_msg_data_t uc_msg, uc_reply;
#endif    
    int c, found=0;
    int priority;

    SET_BFD_INFO;

    if (bfd_info_p->initialized)
    {
        /* Reset everything */

        _bcm_kt_bfd_event_unregister_all(bfd_info_p);

        _bcm_kt_bfd_free_memory(unit, bfd_info_p);
    }

    bfd_info_p->endpoint_count = soc_property_get(unit, spn_BFD_NUM_SESSIONS, 256);
    bfd_info_p->unit = unit;

    /* TODO: use dynamic memory allocation?? */
    bfd_info_p->endpoints =
        _bcm_kt_bfd_alloc_clear(bfd_info_p->endpoint_count *
            sizeof(_bcm_bfd_endpoint_t), "_bcm_bfd_endpoint");

    if (bfd_info_p->endpoints == NULL)
    {
        _bcm_kt_bfd_free_memory(unit, bfd_info_p);
        return BCM_E_MEMORY;
    }

    bfd_info_p->endpoints_in_use =
        _bcm_kt_bfd_alloc_clear(SHR_BITALLOCSIZE(bfd_info_p->endpoint_count),
            "endpoints_in_use");

    if (bfd_info_p->endpoints_in_use == NULL)
    {
        _bcm_kt_bfd_free_memory(unit, bfd_info_p);
        return BCM_E_MEMORY;
    }

    /* initialize BTE */
    bfd_info_p->dmabuf_req = soc_cm_salloc(unit, sizeof(bfd_msg_ctrl_t), "host request");
    if (!bfd_info_p->dmabuf_req) {
        _bcm_kt_bfd_free_memory(unit, bfd_info_p);
        return BCM_E_MEMORY;
    }
    sal_memset(bfd_info_p->dmabuf_req, 0, sizeof(bfd_msg_ctrl_t));

    bfd_info_p->dmabuf_reply = soc_cm_salloc(unit, sizeof(bfd_msg_ctrl_t), "bte reply");
    if (!bfd_info_p->dmabuf_reply) {
        _bcm_kt_bfd_free_memory(unit, bfd_info_p);
        return BCM_E_MEMORY;
    }
    sal_memset(bfd_info_p->dmabuf_reply, 0, sizeof(bfd_msg_ctrl_t));

    bfd_info_p->dma_num = 0;
    bfd_info_p->num_auth_sha1_key = soc_property_get(unit, spn_BFD_SHA1_KEYS, 0);
    bfd_info_p->num_auth_sp_key = soc_property_get(unit, spn_BFD_SIMPLE_PASSWORD_KEYS, 0);
    bfd_info_p->encap_size =  soc_property_get(unit, spn_BFD_ENCAP_MEMORY_SIZE, 1024);

    /* allocate authentication caches */
    if (bfd_info_p->num_auth_sha1_key)
    {
        bfd_info_p->auth_sha1 = _bcm_kt_bfd_alloc_clear(bfd_info_p->num_auth_sha1_key *
                sizeof(bcm_bfd_auth_sha1_t), "auth_sha1");
        if (bfd_info_p->auth_sha1 == NULL)
        {
            _bcm_kt_bfd_free_memory(unit, bfd_info_p);
            return BCM_E_MEMORY;
        }
    }

    if (bfd_info_p->num_auth_sp_key)
    {
        bfd_info_p->auth_sp = _bcm_kt_bfd_alloc_clear(bfd_info_p->num_auth_sp_key *
                sizeof(bcm_bfd_auth_simple_password_t), "auth_simple_password");
        if (bfd_info_p->auth_sp == NULL)
        {
            _bcm_kt_bfd_free_memory(unit, bfd_info_p);
            return BCM_E_MEMORY;
        }
    }


    init_p = &((bfd_msg_ctrl_t*)bfd_info_p->dmabuf_req)->u.in;
    init_p->encap_bytes = bfd_info_p->encap_size;
    init_p->num_auth_sha1_key = bfd_info_p->num_auth_sha1_key;
    init_p->num_auth_sp_key = bfd_info_p->num_auth_sp_key;
    init_p->num_sessions = bfd_info_p->endpoint_count;

    /* Determine which BTE is running BFD.  Try uC to see if the
     * applications start there */
    for (c=0; c < _BFD_NUM_CMICM; c++) {
        result = soc_cmic_uc_appl_init(unit, c, MOS_MSG_CLASS_BFD, _BFD_UC_MSG_TIMEOUT_USECS);
        if (SOC_E_NONE == result){
            /* BFD started successfully */
            soc_cm_print("RECEIVED BFD APPL READY, result: 0x%x\n", result);
            bfd_info_p->bte_num = c;
            found = 1;
            break;
        }
    }


    if (!found) {
        _bcm_kt_bfd_free_memory(unit, bfd_info_p);
        return BCM_E_INTERNAL;
    }

#ifdef BFD_UC_MESSAGE_ON
    /* send init msg to BTE */
    sal_memset(&uc_msg, 0, sizeof(uc_msg));
    sal_memset(&uc_reply, 0, sizeof(uc_reply));
    uc_msg.s.mclass = MOS_MSG_CLASS_BFD;
    uc_msg.s.subclass = MOS_MSG_SUBCLASS_BFD_INIT;
    uc_msg.s.len = bcm_htons(sizeof(bfd_msg_ctrl_init_t));
    uc_msg.s.data = bcm_htonl(soc_cm_l2p(unit, init_p));

    bfd_info_p->dma_num = c * 4;
    init_p->dma_num = bfd_info_p->dma_num;
    soc_cm_sflush(unit, init_p, sizeof(bfd_msg_ctrl_init_t));
    result = soc_cmic_uc_msg_send_receive(unit, c, &uc_msg, &uc_reply, _BFD_UC_MSG_TIMEOUT_USECS);
    soc_cm_print("SENDING INIT_BFD MESSAGE, result: 0x%x\n", result);
    if ((SOC_E_NONE != result) ||
        (uc_reply.s.subclass !=  MOS_MSG_SUBCLASS_BFD_INIT_REPLY) ||
        (uc_reply.s.len != 0) ) {
        /* BFD start failed */
        _bcm_kt_bfd_free_memory(unit, bfd_info_p);
        return BCM_E_INTERNAL;
    }


#endif
    /* setup shared resource manager */
    result = shr_mres_create(&bfd_info_p->res_mgr,
                             BFD_SHR_RES_TYPE_COUNT,
                             BFD_SHR_RES_POOL_COUNT);
    if (BCM_E_NONE !=  result){
        _bcm_kt_bfd_free_memory(unit, bfd_info_p);
        return BCM_E_INTERNAL;
    }
    result = shr_mres_pool_set(bfd_info_p->res_mgr,
                               BFD_SHR_RES_ENCAP_TYPE,
                               SHR_RES_ALLOCATOR_BITMAP,
                               0,
                               bfd_info_p->encap_size,
                               NULL,
                               bfd_msg_encap_pool_name);
    if (BCM_E_NONE !=  result){
        _bcm_kt_bfd_free_memory(unit, bfd_info_p);
        return BCM_E_INTERNAL;
    }
    result = shr_mres_type_set(bfd_info_p->res_mgr,
                               BFD_SHR_RES_ENCAP_TYPE,
                               BFD_SHR_RES_ENCAP_POOL,
                               1 /* element size */,
                               bfd_msg_encap_type_name);
    if (BCM_E_NONE !=  result){
        _bcm_kt_bfd_free_memory(unit, bfd_info_p);
        return BCM_E_INTERNAL;
    }

    /* configure CPU_CONTROL_0 register */

    BCM_IF_ERROR_RETURN(READ_CPU_CONTROL_0r(unit, &val));

    soc_reg_field_set(unit, CPU_CONTROL_0r, &val,
                  BFD_UNKNOWN_VERSION_TOCPUf, 1);
    soc_reg_field_set(unit, CPU_CONTROL_0r, &val,
              BFD_YOUR_DISCRIMINATOR_NOT_FOUND_TOCPUf, 1);
    soc_reg_field_set(unit, CPU_CONTROL_0r, &val,
              BFD_UNKNOWN_CONTROL_PACKET_TOCPUf, 1);
    soc_reg_field_set(unit, CPU_CONTROL_0r, &val,
              BFD_UNKNOWN_ACH_CHANNEL_TYPE_TOCPUf, 1);
    soc_reg_field_set(unit, CPU_CONTROL_0r, &val,
              BFD_UNKNOWN_ACH_VERSION_TOCPUf, 1);

    BCM_IF_ERROR_RETURN(WRITE_CPU_CONTROL_0r(unit, val));

#ifdef BCM_WARM_BOOT_SUPPORT
    if (SOC_WARM_BOOT(unit))
    {
        _bcm_kt_bfd_warm_boot(unit);
    }
#endif /* BCM_WARM_BOOT_SUPPORT */

    /* start event thread handler */
#if 0
    priority = soc_property_get(unit,
                                spn_BCM_BFD_THREAD_PRI,
                                BFD_THREAD_PRI_DFLT);
#else
    priority = BFD_THREAD_PRI_DFLT;
#endif

    bfd_info_p->bfd_tid = sal_thread_create("bcmBFD", SAL_THREAD_STKSZ, 
                            priority,
                            _bcm_kt_bfd_callback_thread, (void*)bfd_info_p);
    if (bfd_info_p->bfd_tid == SAL_THREAD_ERROR) {
        return BCM_E_MEMORY;
    }

    bfd_info_p->initialized = 1;

    return BCM_E_NONE;
}

/*
 * Function:
 *      bcm_kt_bfd_detach
 * Purpose:
 *      Shut down the BFD subsystem
 * Parameters:
 *      unit - (IN) Unit number.
 * Returns:
 *      BCM_E_NONE Operation completed successfully
 * Notes:
 */
int
bcm_kt_bfd_detach(
    int unit)
{

    return BCM_E_NONE;
}

static int _bcm_kt_bfd_find_free_endpoint(SHR_BITDCL *endpoints,
    int endpoint_count, int increment, int offset)
{
    int endpoint_index;

    for (endpoint_index = 0; endpoint_index < endpoint_count;
        endpoint_index += increment)
    {
        if (!SHR_BITGET(endpoints, endpoint_index + offset))
        {
            break;
        }
    }

    if (endpoint_index >= endpoint_count)
    {
        endpoint_index = -1;
    }

    return endpoint_index + offset;
}


static int _bcm_kt_bfd_find_ipv4_entry(int unit, bcm_vrf_t vrf,
    bcm_ip_t ip_addr, int *index_p, l3_entry_ipv4_unicast_entry_t *l3_entry_p)
{
    l3_entry_ipv4_unicast_entry_t l3_key;

    sal_memset(&l3_key, 0, sizeof(l3_entry_ipv4_unicast_entry_t));

    soc_L3_ENTRY_IPV4_UNICASTm_field32_set(unit, &l3_key, VRF_IDf, vrf);

    soc_L3_ENTRY_IPV4_UNICASTm_field32_set(unit, &l3_key, IP_ADDRf, ip_addr);

    soc_L3_ENTRY_IPV4_UNICASTm_field32_set(unit, &l3_key, KEY_TYPEf,
                        TR_L3_HASH_KEY_TYPE_V4UC);

    return soc_mem_search(unit, L3_ENTRY_IPV4_UNICASTm, MEM_BLOCK_ANY,
                        index_p, &l3_key, l3_entry_p, 0);
}

static int _bcm_kt_bfd_find_ipv6_entry(int unit, bcm_vrf_t vrf,
    bcm_ip6_t ip6_addr,int *index_p, l3_entry_ipv6_unicast_entry_t *l3_entry_p)
{
    l3_entry_ipv6_unicast_entry_t l3_key;

    sal_memset(&l3_key, 0, sizeof(l3_entry_ipv6_unicast_entry_t));

    soc_mem_ip6_addr_set(unit, L3_ENTRY_IPV6_UNICASTm,
                    &l3_key, IP_ADDR_LWR_64f,
                    ip6_addr, SOC_MEM_IP6_LOWER_ONLY);

    soc_mem_ip6_addr_set(unit, L3_ENTRY_IPV6_UNICASTm,
                    &l3_key, IP_ADDR_UPR_64f,
                    ip6_addr, SOC_MEM_IP6_UPPER_ONLY);

    soc_L3_ENTRY_IPV6_UNICASTm_field32_set(unit, &l3_key,
                        KEY_TYPE_1f,
                        TR_L3_HASH_KEY_TYPE_V6UC);

    soc_L3_ENTRY_IPV6_UNICASTm_field32_set(unit, &l3_key,
                        KEY_TYPE_0f,
                        TR_L3_HASH_KEY_TYPE_V6UC);

    soc_L3_ENTRY_IPV6_MULTICASTm_field32_set(unit,
                         &l3_key, VRF_IDf, vrf);

    return soc_mem_search(unit, L3_ENTRY_IPV6_UNICASTm, MEM_BLOCK_ANY,
                        index_p, &l3_key, l3_entry_p, 0);

}

static int _bcm_kt_bfd_find_l2x_entry(int unit, uint32 key, int key_type,
    int ses_type, int *index_p, l2x_entry_t *l2_entry_p)
{
    l2x_entry_t l2_key;

     sal_memset(&l2_key, 0, sizeof(l2_key));

     soc_mem_field32_set(unit, L2Xm, &l2_key, KEY_TYPEf, key_type);
     soc_mem_field32_set(unit, L2Xm, &l2_key,
                         SESSION_IDENTIFIER_TYPEf, ses_type);

     if(ses_type)
        soc_mem_field32_set(unit, L2Xm, &l2_key, LABELf, key);
     else
        soc_mem_field32_set(unit, L2Xm, &l2_key,
                            YOUR_DISCRIMINATORf, key);

      return soc_mem_search(unit, L2Xm, MEM_BLOCK_ANY, index_p, &l2_key, l2_entry_p, 0);

}


static int _bcm_kt_bfd_destroy_endpoint(int unit,
    _bcm_bfd_endpoint_t *endpoint_p)
{
    _bcm_bfd_info_t *bfd_info_p;
    bcm_module_t module_id;
    bcm_port_t port_id;
    int l2_index = 0;
    int result = SOC_E_NONE;
    int is_local = 0;
    int l3_index = 0;
    l2x_entry_t l2_entry;
    l3_entry_ipv4_unicast_entry_t l3_entry_v4;
    l3_entry_ipv6_unicast_entry_t l3_entry_v6;
    int v6 = 0;
#if defined(BCM_TRIUMPH_SUPPORT) && defined(BCM_MPLS_SUPPORT)
    mpls_entry_entry_t mpls_entry;
    mpls_entry_entry_t mpls_key;
    int mpls_index = 0;
#endif /* BCM_TRIUMPH_SUPPORT &&  BCM_MPLS_SUPPORT */
#if defined(BCM_CMICM_SUPPORT)
    mos_msg_data_t uc_msg, uc_reply;
#endif

    SET_BFD_INFO;


    if (endpoint_p->glp & (1 << 13))
    {
        /* Trunk */
    }
    else {
    module_id = (endpoint_p->glp & 0x0FC0) >> 6;
    port_id = (endpoint_p->glp & 0x3F);
    result = _bcm_esw_modid_is_local(unit, module_id, &is_local);
    }

   switch(endpoint_p->info.type) {
    case bcmBFDTunnelTypeUdp:
        /* check whether v6 or v4 */
        if (endpoint_p->info.flags & BCM_BFD_ENDPOINT_IPV6){
            v6 = 1;
        }
            /* L3 entry */
        _bcm_esw_l3_lock(unit);

        if(!v6) {
            if (BCM_SUCCESS(_bcm_kt_bfd_find_ipv4_entry(unit,
                                                    endpoint_p->info.vrf_id,
                                                    endpoint_p->info.src_ip_addr,
                                                    &l3_index, &l3_entry_v4))){
                /* reset BFD_ENABLE and LOCAL_ADDRESS */
            result = soc_mem_field32_modify(unit, L3_ENTRY_IPV4_UNICASTm,
                                            l3_index, LOCAL_ADDRESSf, 0);
            result |= soc_mem_field32_modify(unit, L3_ENTRY_IPV4_UNICASTm,
                                            l3_index, BFD_ENABLEf, 0);
        } else {
                _bcm_esw_l3_unlock(unit);
                return BCM_E_PARAM;

        }
        }
        else {
            if (BCM_SUCCESS(_bcm_kt_bfd_find_ipv6_entry(unit,
                                                     endpoint_p->info.vrf_id,
                                                     endpoint_p->info.src_ip6_addr,
                                                     &l3_index, &l3_entry_v6))){
                    /* reset BFD_ENABLE and LOCAL_ADDRESS */
                result = soc_mem_field32_modify(unit, L3_ENTRY_IPV6_UNICASTm,
                                                    l3_index, LOCAL_ADDRESSf, 0);
                result |= soc_mem_field32_modify(unit, L3_ENTRY_IPV6_UNICASTm,
                                                l3_index, BFD_ENABLEf, 0);

            }
            else {
                _bcm_esw_l3_unlock(unit);
                return BCM_E_PARAM;
            }

            }

            _bcm_esw_l3_unlock(unit);

            if (BCM_SUCCESS(_bcm_kt_bfd_find_l2x_entry(unit,
                                            endpoint_p->info.local_discr, 4, 0,
                                            &l2_index, &l2_entry))){

                soc_mem_delete_index(unit, L2Xm, MEM_BLOCK_ANY, l2_index);

            } else {

                return BCM_E_PARAM;

            }
            break;
        case bcmBFDTunnelTypeIp4in4:
        case bcmBFDTunnelTypeIp6in4:
        case bcmBFDTunnelTypeIp4in6:
        case bcmBFDTunnelTypeIp6in6:
            break;
        case bcmBFDTunnelTypeGRE:
            break;
        case bcmBFDTunnelTypeMpls:
        case bcmBFDTunnelTypeMplsTpCc:
        case bcmBFDTunnelTypeMplsTpCcCv:
        case bcmBFDTunnelTypePweControlWord:
        case bcmBFDTunnelTypePweRouterAlert:
        case bcmBFDTunnelTypePweTtl:
#if defined(BCM_TRIUMPH_SUPPORT) && defined(BCM_MPLS_SUPPORT)
            SOC_IF_ERROR_RETURN(bcm_tr_mpls_lock (unit));

            sal_memset(&mpls_key, 0, sizeof(mpls_key));
            soc_MPLS_ENTRYm_field32_set(unit, &mpls_key, MPLS_LABELf, endpoint_p->info.label);

            SOC_IF_ERROR_RETURN(soc_mem_search(unit, MPLS_ENTRYm,
                                               MEM_BLOCK_ANY, &mpls_index,
                                               &mpls_key, &mpls_entry, 0));

            if ((soc_MPLS_ENTRYm_field32_get(unit,
                   &mpls_entry, VALIDf) != 0x1)) {
                bcm_tr_mpls_unlock (unit);
                return BCM_E_PARAM;
            }

            soc_MPLS_ENTRYm_field32_set(unit, &mpls_entry, BFD_ENABLEf, 0);

            soc_MPLS_ENTRYm_field32_set(unit, &mpls_entry, PW_CC_TYPEf, 0);

            SOC_IF_ERROR_RETURN(soc_mem_write(unit, MPLS_ENTRYm,
                                            MEM_BLOCK_ANY, mpls_index, &mpls_entry));

            bcm_tr_mpls_unlock (unit);

            if (BCM_SUCCESS(_bcm_kt_bfd_find_l2x_entry(unit,
                                                    endpoint_p->info.label, 4, 1,
                                                    &l2_index, &l2_entry))){
                soc_mem_delete_index(unit, L2Xm, MEM_BLOCK_ANY, l2_index);

            } else {

                    return BCM_E_PARAM;

            }
#endif /* BCM_TRIUMPH_SUPPORT &&  BCM_MPLS_SUPPORT */
            break;
        default:
            break;

        }

#if defined(BCM_CMICM_SUPPORT)
    /* destroy BTE session */
    sal_memset(&uc_msg, 0, sizeof(uc_msg));
    uc_msg.s.mclass = MOS_MSG_CLASS_BFD;
    uc_msg.s.subclass = MOS_MSG_SUBCLASS_BFD_SESS_DELETE;
    uc_msg.s.len = bcm_htons(endpoint_p->endpoint_index);
    uc_msg.s.data = 0;

    sal_memset(&uc_reply, 0, sizeof(uc_reply));

#ifdef BFD_UC_MESSAGE_ON
    result = soc_cmic_uc_msg_send_receive(unit, bfd_info_p->bte_num, &uc_msg, &uc_reply, _BFD_UC_MSG_TIMEOUT_USECS);
    if ( (SOC_E_NONE == result) &&
         (uc_reply.s.subclass == MOS_MSG_SUBCLASS_BFD_SESS_DELETE_REPLY) &&
         (uc_reply.s.mclass == MOS_MSG_CLASS_BFD) ) {
    }else{
        result = BCM_E_INTERNAL;
    }
#endif
    /* free encapsulation data space */
    result = shr_mres_free(bfd_info_p->res_mgr,
                           BFD_SHR_RES_ENCAP_TYPE,
                           endpoint_p->l2l3l4_length,
                           endpoint_p->l2l3l4_offset);
    if (BCM_E_NONE != result) {
        result  = BCM_E_INTERNAL;
    }
#endif

    SHR_BITCLR(bfd_info_p->endpoints_in_use, endpoint_p->endpoint_index);
    endpoint_p->endpoint_index = -1;

    return BCM_E_NONE;
}
/* static */
int
_bcm_kt_bfd_encap_set(int unit, bcm_port_t port_id, _bcm_bfd_endpoint_t *endpoint_p)
{
    int result = BCM_E_NONE;
#if defined(BCM_CMICM_SUPPORT)
    _bcm_bfd_info_t *bfd_info_p;
    bcm_bfd_endpoint_info_t *endpoint_info;
    bcm_l3_egress_t l3_egress;
    bcm_l3_intf_t l3_intf;
    bcm_mpls_egress_label_t label_array[_BFD_MPLS_MAX_LABELS];
    bcm_mpls_port_t mpls_port;
    int label_count, label=0;
    _l2_hdr_t l2;
    uint32 vlan;
    uint32 mpls[_BFD_MPLS_MAX_LABELS];
    uint32 ach = 0;
    _ipv4_hdr_t ipv4;
    _ipv6_hdr_t ipv6;
    _udp_hdr_t udp;
    int ip_header_length = 0;
    int bfd_length = 0;
    bfd_msg_ctrl_encap_t *encap_p;
    mos_msg_data_t uc_msg, uc_reply;
    uint16 tpid;
    int i, j, ip_flag=0, ip_egress_lookup=0, v6=0, mpls_mep_flag=0;
    int mpls_flag=0, mpls_ach_flag=0, mpls_alert_flag=0, mpls_gal_flag=0;
    uint16 etype = 0;
    int encap_length, encap_base, mpls_mep_offset=0;
    int auth_len = 0;

    uint32 word = 0;

    SET_BFD_INFO;

    sal_memset(&l2, 0, sizeof(l2));
    sal_memset(&vlan, 0, sizeof(vlan));
    bcm_l3_intf_t_init(&l3_intf);
    sal_memset(mpls, 0, sizeof(mpls));

    endpoint_info = &endpoint_p->info;

    /* check for incompatible flags */
    if ( (endpoint_info->flags & BCM_BFD_ENDPOINT_PWE_RAW) &&
         (endpoint_info->flags & BCM_BFD_ENDPOINT_IPV6) ) {
        return BCM_E_PARAM;
    }

    /* validate parameters, set flags */
    switch (endpoint_info->type) {
    case bcmBFDTunnelTypeUdp:
        VALIDATE_UDP_SRCPORT(endpoint_info->udp_src_port);
        if (endpoint_info->flags & BCM_BFD_ENDPOINT_IPV6) {
            v6 = 1;
        }
        ip_flag = 1;
        ip_egress_lookup = 1;
        if ( 0 == (endpoint_info->flags & BCM_BFD_ENDPOINT_MULTIHOP)) {
            endpoint_info->ip_ttl = 1;
        }
        break;
    case bcmBFDTunnelTypeIp4in4:
        break;
    case bcmBFDTunnelTypeIp6in4:
        break;
    case bcmBFDTunnelTypeIp4in6:
        break;
    case bcmBFDTunnelTypeIp6in6:
        break;
    case bcmBFDTunnelTypeGRE:
        break;
    case bcmBFDTunnelTypeMpls:
        VALIDATE_UDP_SRCPORT(endpoint_info->udp_src_port);
        VALIDATE_IPV4_LOOPBACK(endpoint_info->dst_ip_addr);
        VALIDATE_IPV4_ROUTABLE(endpoint_info->src_ip_addr);
        endpoint_info->ip_ttl = 1;
        if (endpoint_info->flags & BCM_BFD_ENDPOINT_IPV6) {
            v6 = 1;
        }
        ip_flag = 1;
        mpls_flag = 1;
        break;
    case bcmBFDTunnelTypePweControlWord:
        mpls_flag = 1;
        mpls_ach_flag = 1;   /* mandatory */
        if (endpoint_info->flags & BCM_BFD_ENDPOINT_PWE_RAW) {
            return BCM_E_PARAM;
        }
        if (!BCM_GPORT_IS_MPLS_PORT(endpoint_info->gport)) {
            return BCM_E_PARAM;
        }
        ip_flag = 1;
        VALIDATE_UDP_SRCPORT(endpoint_info->udp_src_port);
        if (endpoint_info->flags & BCM_BFD_ENDPOINT_IPV6) {
            v6 = 1;
            /* TODO: validate addresses */
        }else{
            VALIDATE_IPV4_LOOPBACK(endpoint_info->dst_ip_addr);
            VALIDATE_IPV4_ROUTABLE(endpoint_info->src_ip_addr);
        }
        endpoint_info->ip_ttl = 255;
        break;
    case bcmBFDTunnelTypePweRouterAlert:
        mpls_alert_flag  = 1;
        /* fall through */
    case bcmBFDTunnelTypePweTtl:
        mpls_flag = 1;
        if (endpoint_info->flags & BCM_BFD_ENDPOINT_PWE_RAW) {
            if (!BCM_GPORT_IS_MPLS_PORT(endpoint_info->gport)) {
                return BCM_E_PARAM;
            }
            mpls_ach_flag = 1;
        }else {
            ip_flag = 1;
            VALIDATE_UDP_SRCPORT(endpoint_info->udp_src_port);
            if (endpoint_info->flags & BCM_BFD_ENDPOINT_IPV6) {
                v6 = 1;
                /* TODO: validate addresses */
            }else{
                VALIDATE_IPV4_LOOPBACK(endpoint_info->dst_ip_addr);
                VALIDATE_IPV4_ROUTABLE(endpoint_info->src_ip_addr);
            }
            if (endpoint_info->flags & BCM_BFD_ENDPOINT_PWE_ACH) {
                mpls_ach_flag = 1;
            }
        }
        endpoint_info->ip_ttl = 255;
        break;
    case bcmBFDTunnelTypeMplsTpCc:
        mpls_flag = 1;
        mpls_ach_flag = 1; /* mandatory */
        mpls_gal_flag = 1;
        break;
    case bcmBFDTunnelTypeMplsTpCcCv:
        if ( (0 == endpoint_info->mep_id_length) ||
             (BCM_BFD_ENDPOINT_MAX_MEP_ID_LENGTH < endpoint_info->mep_id_length) ){
            return BCM_E_PARAM;
        }
        mpls_flag = 1;
        mpls_ach_flag = 1; /* mandatory */
        mpls_gal_flag = 1;
        mpls_mep_flag = 1;
        break;
    default:
        return BCM_E_PARAM;
    }

    if ( (!mpls_mep_flag) && 
         (endpoint_info->mep_id_length) ) {
        return BCM_E_PARAM;
    }

    /* IP/UDP info */
    if (ip_flag) {

        if (ip_egress_lookup) {
            sal_memset(&l3_egress, 0, sizeof(l3_egress));
            if (BCM_SUCCESS(bcm_esw_l3_egress_get(unit, endpoint_info->egress_if, &l3_egress))) {
            }else{
                return BCM_E_PARAM;
            }
            l3_intf.l3a_intf_id = l3_egress.intf;
            if (BCM_FAILURE(bcm_esw_l3_intf_get(unit, &l3_intf))) {
                return BCM_E_PARAM;
            }
        }

        sal_memset(&ipv4, 0, sizeof(ipv4));
        sal_memset(&udp, 0, sizeof(udp));

        if (!v6) {
            endpoint_p->l2l3l4_type = BFD_ENCAP_TYPE_V4UDP;
            etype = _BFD_IPV4_ETYPE;
            ipv4.v_hl = _BFD_IPV4_VERSION_LENGTH;
            ipv4.tos = endpoint_info->ip_tos;
        }else{
            /* TODO: v6 */
            endpoint_p->l2l3l4_type = BFD_ENCAP_TYPE_V6UDP;
        }

        /* Set length fields:
         *   - IP total length = IP + UDP + BFD
         *   - UDP length = UDP + BFD
         */
        switch (endpoint_info->auth) {
        case BFD_AUTH_TYPE_SIMPLE_PASSWORD:
            auth_len = bfd_info_p->auth_sp[endpoint_info->auth_index].length;
            bfd_length = _BFD_BASE_HEADER_LENGTH
                + _BFD_AUTH_SP_HEADER_START_LENGTH + auth_len;
            break;
        case BFD_AUTH_TYPE_KEYED_SHA1:
        case BFD_AUTH_TYPE_METICULOUS_KEYED_SHA1:
            bfd_length =  _BFD_BASE_HEADER_LENGTH + _BFD_AUTH_SHA1_LENGTH;
            break;
        default:
            bfd_length = _BFD_BASE_HEADER_LENGTH;
        }

        /* UDP Header */
        sal_memset(&udp, 0, sizeof(udp));
        udp.src = endpoint_info->udp_src_port;
        udp.dst = (endpoint_info->flags & BCM_BFD_ENDPOINT_MULTIHOP) ?
            _BFD_UDP_MULTI_HOP_DEST_PORT : _BFD_UDP_SINGLE_HOP_DEST_PORT;
        udp.len = _BFD_UDP_HEADER_LENGTH + bfd_length;
        udp.cksum = 0; /* calculated in BTE */

        /* IP Header */
        if (v6) {
            sal_memset(&ipv6, 0, sizeof(ipv6));
            ip_header_length = _BFD_IPV6_HEADER_LENGTH;
            etype = _BFD_IPV6_ETYPE;
            ipv6.version = _BFD_IPV6_VERSION;
            ipv6.tclass = 0;  /* TO DO */
            ipv6.flable = 0;  /* TO DO */
            ipv6.plength = udp.len;
            ipv6.nexthdr = _BFD_IPV4_UDP_PROTOCOL;
            ipv6.hoplimit = endpoint_info->ip_ttl;
            sal_memcpy(ipv6.src, endpoint_info->src_ip6_addr, BCM_IP6_ADDRLEN);
            sal_memcpy(ipv6.dst, endpoint_info->dst_ip6_addr, BCM_IP6_ADDRLEN);
        } else {
            sal_memset(&ipv4, 0, sizeof(ipv4));
            ip_header_length = _BFD_IPV4_HEADER_LENGTH;
            etype = _BFD_IPV4_ETYPE;
            ipv4.v_hl = _BFD_IPV4_VERSION_LENGTH;
            ipv4.tos = endpoint_info->ip_tos;
            ipv4.len = _BFD_IPV4_HEADER_LENGTH + udp.len;
            ipv4.id = 0;
            ipv4.off = 0;
            ipv4.ttl = endpoint_info->ip_ttl;
            ipv4.p = _BFD_IPV4_UDP_PROTOCOL;
            ipv4.sum = 0;
            ipv4.src = endpoint_info->src_ip_addr;
            ipv4.dst = endpoint_info->dst_ip_addr;
            ipv4.sum = _shr_ip_chksum(20, (uint8*)&ipv4);
        }

    }

    /* MPLS */
    if (mpls_flag) {
        etype = _BFD_MPLS_ETYPE;

        /* Build ACH */
        if (mpls_ach_flag) {
            ach = _BFD_ACH_PWACH_VERSION << 24;
            if (endpoint_info->flags & BCM_BFD_ENDPOINT_PWE_RAW) {
                ach |= _BFD_ACH_CHANNEL_TYPE_RAW;
            }else if (v6) {
                ach |= _BFD_ACH_CHANNEL_TYPE_IPV6;
            }else{
                ach |= _BFD_ACH_CHANNEL_TYPE_IPV4;
            }

        }

        /* PWE and MPLS-TP cases use gport to resolve interface */
        if (BCM_GPORT_IS_MPLS_PORT(endpoint_info->gport)) {
            /* Get mpls port and label info */
            bcm_mpls_port_t_init(&mpls_port);
            mpls_port.mpls_port_id = endpoint_info->gport;
            if (BCM_FAILURE(bcm_esw_mpls_port_get(unit, endpoint_info->vpn, &mpls_port))) {
                return BCM_E_PARAM;
            }else{
                if (endpoint_info->type == bcmBFDTunnelTypePweTtl) {
                    mpls_port.egress_label.ttl = 0x1;
                }
                mpls[label] = ((mpls_port.egress_label.label & 0xfffff) << 12) |
                    ((mpls_port.egress_label.exp & 0x7) << 9) |
                    (mpls_port.egress_label.ttl & 0xff);
                label++;
            }

            if (BCM_FAILURE(bcm_esw_l3_egress_get(unit, mpls_port.egress_tunnel_if, &l3_egress))) {
                return BCM_E_PARAM;
            }

            l3_intf.l3a_intf_id = l3_egress.intf;
            if (BCM_FAILURE(bcm_esw_l3_intf_get(unit, &l3_intf))) {
                return BCM_E_PARAM;
            }
        }else{
            /* MPLS */
            if (BCM_FAILURE(bcm_esw_l3_egress_get(unit, endpoint_info->egress_if, &l3_egress))) {
                return BCM_E_PARAM;
            }

            l3_intf.l3a_intf_id = l3_egress.intf;
            if (BCM_FAILURE(bcm_esw_l3_intf_get(unit, &l3_intf))) {
                return BCM_E_PARAM;
            }
        }

        /* look for a tunnel associated with this interface */
        if (BCM_FAILURE(bcm_esw_mpls_tunnel_initiator_get(unit, l3_egress.intf,
                                                      _BFD_MPLS_MAX_LABELS,
                                                      label_array,
                                                      &label_count))) {
        }else{
            for (i=0; i<label_count; i++) {
                mpls[label] = ((label_array[i].label & 0xfffff) << 12) |
                    ((label_array[i].exp & 0x7) << 9) |
                    (label_array[i].ttl & 0xff);
                label++;
            }

        }

        mpls[0] |= 0x1 << 8; /* set stack bit */

    }

    /* L2 */
    sal_memcpy(l2.dst_mac, l3_egress.mac_addr, 6);
    sal_memcpy(l2.src_mac, l3_intf.l3a_mac_addr, 6);
    if (BCM_FAILURE(bcm_esw_port_tpid_get(unit, port_id, &tpid))) {
        return BCM_E_INTERNAL;
    }
    l2.etype = bcm_htons(tpid);
    vlan = ((endpoint_info->vlan_pri & 0x7) << 28) |
           ((l3_intf.l3a_vid & 0xfff) << 16) |
           (etype & 0xffff);

    /* allocate space in encapsulation table */
    encap_length = _BFD_L2_HEADER_LENGTH + _BFD_VLAN_HEADER_LENGTH;
    encap_length += (ip_flag) ? (ip_header_length + _BFD_UDP_HEADER_LENGTH) : 0;
    encap_length += (mpls_ach_flag) ? (_BFD_ACH_HEADER_LENGTH) : 0;
    encap_length += (mpls_flag) ? (_BFD_MPLS_HEADER_LENGTH * label) : 0;
    encap_length += (mpls_alert_flag) ? 4 : 0;
    encap_length += (mpls_gal_flag) ? 4 : 0;
    if (mpls_mep_flag) {
        mpls_mep_offset = encap_length;
        encap_length += endpoint_info->mep_id_length;
    }

    result = shr_mres_alloc(bfd_info_p->res_mgr,
                            BFD_SHR_RES_ENCAP_TYPE,
                            0,
                            encap_length,
                            &encap_base);
    if (BCM_E_NONE !=  result){
        return BCM_E_RESOURCE;
    }

    encap_p = &((bfd_msg_ctrl_t*)bfd_info_p->dmabuf_req)->u.enc;
    sal_memset(encap_p, 0, sizeof(bfd_msg_ctrl_encap_t));

    encap_p->offset = encap_base;
    encap_p->length = encap_length;
    i = 0;
    sal_memcpy(&encap_p->data[i], &l2.dst_mac, sizeof(bcm_mac_t));
    i+=sizeof(bcm_mac_t);
    sal_memcpy(&encap_p->data[i], &l2.src_mac, sizeof(bcm_mac_t));
    i+=sizeof(bcm_mac_t);
    sal_memcpy(&encap_p->data[i], &l2.etype, sizeof(uint16));
    i += sizeof(uint16);

    vlan = bcm_htonl(vlan);
    sal_memcpy(&encap_p->data[i], &vlan, sizeof(uint32));
    i += sizeof(uint32);
    if (mpls_flag) {
        for (j=label-1; j >= 0; j--) {
            word = bcm_htonl(mpls[j]);
            sal_memcpy(&encap_p->data[i], &word, sizeof(uint32));
            i += sizeof(uint32);
            if ( mpls_alert_flag && (j==(label-1)) ){
                word = bcm_htonl((_BFD_MPLS_ROUTER_ALERT_LABEL << 12) | 0x1);
                sal_memcpy(&encap_p->data[i], &word, sizeof(uint32));
                i += sizeof(uint32);
            }
        }
        if (mpls_gal_flag) {
            if ( mpls_alert_flag && (j==(label-1)) ){
                word = bcm_htonl((_BFD_MPLS_GAL_LABEL << 12) | 0x1);
                sal_memcpy(&encap_p->data[i], &word, sizeof(uint32));
                i += sizeof(uint32);
            }
        }
        if (mpls_ach_flag) {
            word = bcm_htonl(ach);
            sal_memcpy(&encap_p->data[i], &word, sizeof(uint32));
            i += sizeof(uint32);
        }
        if (mpls_mep_flag) {
            /* TODO: set the channel type in the ACH correctly based on CC/CV */
            sal_memcpy(&encap_p->data[mpls_mep_offset],  \
                    endpoint_info->mep_id, endpoint_info->mep_id_length);
        }
    }
    if (ip_flag) {

        if (v6) {
            word = bcm_htonl((ipv6.version << 28) |
                             (ipv6.tclass << 20) |
                             (ipv6.flable));
            sal_memcpy(&encap_p->data[i], &word, 4);
            i += 4;
            word = bcm_htonl((ipv6.plength << 16) |
                             (ipv6.nexthdr << 8) |
                             (ipv6.hoplimit));
            sal_memcpy(&encap_p->data[i], &word, 4);
            i += 4;
            sal_memcpy(&encap_p->data[i], ipv6.src, BCM_IP6_ADDRLEN);
            i += BCM_IP6_ADDRLEN;
            sal_memcpy(&encap_p->data[i], ipv6.dst, BCM_IP6_ADDRLEN);
            i += BCM_IP6_ADDRLEN;

        } else {
            word = bcm_htonl((ipv4.v_hl << 24) |
                             (ipv4.tos << 16) |
                             (ipv4.len));
            sal_memcpy(&encap_p->data[i], &word, 4);
            i+=4;
            word = bcm_htonl((ipv4.id << 16) |
                             (ipv4.off));
            sal_memcpy(&encap_p->data[i], &word, 4);
            i+=4;
            word = bcm_htonl((ipv4.ttl << 24) |
                             (ipv4.p << 16) |
                             (ipv4.sum));
            sal_memcpy(&encap_p->data[i], &word, 4);
            i+=4;
            word = bcm_htonl(ipv4.src);
            sal_memcpy(&encap_p->data[i], &word, 4);
            i+=4;
            word = bcm_htonl(ipv4.dst);
            sal_memcpy(&encap_p->data[i], &word, 4);
            i+=4;
        }

        word = bcm_htonl((udp.src << 16) | udp.dst);
        sal_memcpy(&encap_p->data[i], &word, 4);
        i+=4;
        word = bcm_htonl((udp.len << 16) | udp.cksum);
        sal_memcpy(&encap_p->data[i], &word, 4);
        i+=4;
    }

    assert(i == encap_length);

#ifdef BCMSIM_DUMP_ENCAP
    {
        int t;

        soc_cm_print("L2-L4 header for BFD encap:\n");
        soc_cm_print("L2:      ");
        for (i = 0; i < 14; i++) {
            soc_cm_print("0x%02x ", encap_p->data[i]);
        }
        t = i;
        soc_cm_print("\nVLAN:    ");
        for (i = t; i < (t + sizeof(uint32)); i++) {
            soc_cm_print("0x%02x ", encap_p->data[i]);
        }
        t += i - t;
        soc_cm_print("\n");
        if (mpls_flag) {
            soc_cm_print("MPLS:    ");
            if (mpls_alert_flag) {
                label++;
            }
            for (i = t; i < (t + (sizeof(uint32) * label)); i++) {
                soc_cm_print("0x%02x ", encap_p->data[i]);
            }
            t += i - t;
            soc_cm_print("\n");
        }
        if (mpls_ach_flag) {
            soc_cm_print("ACH:     ");
            for (i = t; i < (t + sizeof(uint32)); i++) {
                soc_cm_print("0x%02x ", encap_p->data[i]);
            }
            t += i - t;
            soc_cm_print("\n");
        }
        if (mpls_mep_flag) {
            soc_cm_print("MEP-ID:  ");
            for (i = t; i < (t + endpoint_info->mep_id_length); i++) {
                if (( (i-t) & 0xf) == 0) {
                    soc_cm_print("\n");
                }
                soc_cm_print("0x%02x ", encap_p->data[i]);
            }
            t += i - t;
            soc_cm_print("\n");
        }
        if (ip_flag) {
            if (v6) {
                soc_cm_print("IPV6:   ");
                for (i = t; i < (t + _BFD_IPV6_HEADER_LENGTH); i++) {
                    if (( (i-t) & 0xf) == 0) {
                        soc_cm_print("\n");
                    }
                    soc_cm_print("0x%02x ", encap_p->data[i]);
                }
                t += i - t;
            } else {
                soc_cm_print("IPV4:   ");
                for (i = t; i < (t + 20); i++) {
                    if (( (i-t) & 0xf) == 0) {
                        soc_cm_print("\n");
                    }
                    soc_cm_print("0x%02x ", encap_p->data[i]);
                }
                t += i - t;
            }

            soc_cm_print("\nUDP:   ");
            for (i = t; i < (t + 8); i++) {
                if (( (i-t) & 0xf) == 0) {
                    soc_cm_print("\n");
                }
                soc_cm_print("0x%02x ", encap_p->data[i]);
            }
            soc_cm_print("\n");
        }
    }
#endif /* BCMSIM_DUMP_ENCAP */

    /* send encap message to BTE */
    sal_memset(&uc_msg, 0, sizeof(uc_msg));
    uc_msg.s.mclass = MOS_MSG_CLASS_BFD;
    uc_msg.s.subclass = MOS_MSG_SUBCLASS_BFD_ENCAP_SET;
    uc_msg.s.len = bcm_htons(sizeof(bfd_msg_ctrl_encap_t));
    uc_msg.s.data = bcm_htonl(soc_cm_l2p(unit, encap_p));

    sal_memset(&uc_reply, 0, sizeof(uc_reply));

#ifdef BFD_UC_MESSAGE_ON
    result = soc_cmic_uc_msg_send_receive(unit, bfd_info_p->bte_num, &uc_msg, &uc_reply, _BFD_UC_MSG_TIMEOUT_USECS);
#else 
    result = SOC_E_NONE;
    uc_reply.s.subclass = MOS_MSG_SUBCLASS_BFD_ENCAP_SET_REPLY;
    uc_reply.s.mclass = MOS_MSG_CLASS_BFD;
#endif
    if ( (SOC_E_NONE == result) &&
         (uc_reply.s.subclass == MOS_MSG_SUBCLASS_BFD_ENCAP_SET_REPLY) &&
         (uc_reply.s.mclass == MOS_MSG_CLASS_BFD) ) {
        ;
    }else{
        result = BCM_E_INTERNAL;
        shr_mres_free(bfd_info_p->res_mgr,
                      BFD_SHR_RES_ENCAP_TYPE,
                      encap_length,
                      encap_base);
        return result;
    }

    /* free prior encapsulation space */
    if (endpoint_p->l2l3l4_length) {
        result = shr_mres_free(bfd_info_p->res_mgr,
                                BFD_SHR_RES_ENCAP_TYPE,
                                endpoint_p->l2l3l4_length,
                                endpoint_p->l2l3l4_offset);
        if (BCM_E_NONE !=  result){
            return BCM_E_RESOURCE;
        }
    }

    endpoint_p->l2l3l4_offset = encap_base;
    endpoint_p->l2l3l4_length = encap_length;
    endpoint_p->mep_offset = mpls_mep_offset;
#endif /*BCM_CMICM_SUPPORT */

    return result;
}

static int _bcm_kt_bfd_get_endpoint(int unit, int endpoint_index,
    _bcm_bfd_endpoint_t *endpoint_p, bcm_bfd_endpoint_info_t *endpoint_info)
{
    _bcm_bfd_info_t *bfd_info_p;
    mos_msg_data_t uc_msg, uc_reply;
    bfd_msg_ctrl_sess_get_reply_t *get_rpy;
    int result = BCM_E_NONE;

    SET_BFD_INFO;

    sal_memcpy(endpoint_info, &endpoint_p->info,
               sizeof(bcm_bfd_endpoint_info_t));

    get_rpy = &((bfd_msg_ctrl_t*)bfd_info_p->dmabuf_reply)->u.get_rpy;
    sal_memset(get_rpy, 0, sizeof(bfd_msg_ctrl_sess_get_reply_t));
    soc_cm_sflush(unit, get_rpy, sizeof(bfd_msg_ctrl_sess_get_reply_t));

    sal_memset(&uc_msg, 0, sizeof(uc_msg));
    sal_memset(&uc_reply, 0, sizeof(uc_reply));
    uc_msg.s.mclass = MOS_MSG_CLASS_BFD;
    uc_msg.s.subclass = MOS_MSG_SUBCLASS_BFD_SESS_GET;
    uc_msg.s.len = bcm_htons(endpoint_index);
    uc_msg.s.data = bcm_htonl(soc_cm_l2p(unit, get_rpy));

#ifdef BFD_UC_MESSAGE_ON
    result = soc_cmic_uc_msg_send_receive(unit, bfd_info_p->bte_num, &uc_msg, &uc_reply, _BFD_UC_MSG_TIMEOUT_USECS);
    if ((SOC_E_NONE == result) &&
        (uc_reply.s.mclass == MOS_MSG_CLASS_BFD) &&
        (uc_reply.s.subclass == MOS_MSG_SUBCLASS_BFD_SESS_GET_REPLY) &&
        (uc_reply.s.len == (sizeof(bfd_msg_ctrl_sess_get_reply_t)<<8) )) {

        /* copy out BFD protocol state information */
        endpoint_info->local_state = get_rpy->local_sess_state;
        endpoint_info->local_discr = get_rpy->local_discriminator;
        endpoint_info->local_diag = get_rpy->local_diag;
        endpoint_info->tx_auth_seq = get_rpy->xmt_auth_seq;
        endpoint_info->rx_auth_seq = get_rpy->rcv_auth_seq;
        endpoint_info->remote_discr = get_rpy->remote_discriminator;
        endpoint_info->remote_min_tx = get_rpy->remote_min_tx;
        endpoint_info->remote_min_rx = get_rpy->remote_min_rx;
        endpoint_info->remote_min_echo = get_rpy->remote_min_echo_rx;
        endpoint_info->remote_detect_mult = get_rpy->remote_detect_mult;
        endpoint_info->remote_diag = get_rpy->remote_diag;

        result = BCM_E_NONE;
    } else {
        result = BCM_E_INTERNAL;
    }
#endif

    return result;
}
/*
 * Function:
 *      bcm_kt_bfd_endpoint_create
 * Purpose:
 *      Create or update an BFD endpoint object
 * Parameters:
 *      unit - (IN) Unit number.
 *      endpoint_info - (IN/OUT) Pointer to an BFD endpoint structure
 * Returns:
 *      BCM_E_NONE Operation completed successfully
 *      BCM_E_PARAM Invalid parameter specified
 *      BCM_E_NOT_FOUND Attempt to update an endpoint which does not exist
 *      BCM_E_EXISTS Attempt to create an endpoint with a specified ID which is already in use
 *      BCM_E_FULL No free endpoints available.
 *
 * Notes:
 */
int
bcm_kt_bfd_endpoint_create(
    int unit,
    bcm_bfd_endpoint_info_t *endpoint_info)
{
    _bcm_bfd_info_t *bfd_info_p;
     _bcm_bfd_endpoint_t *endpoint_p;
    int update;
    int endpoint_index;
    bcm_module_t module_id;
    bcm_port_t port_id;
    bcm_trunk_t trunk_id;
    int local_id;
    l3_entry_ipv4_unicast_entry_t l3_entry_v4;
    l3_entry_ipv6_unicast_entry_t l3_entry_v6;
    int l3_index = 0;
    int v6 = 0;
    int result = SOC_E_NONE;
    l2x_entry_t l2_entry;
    uint32 glp = 0;
    int is_local = 1;
    bcm_l3_egress_t l3_egress;
#if defined(BCM_TRIUMPH_SUPPORT) && defined(BCM_MPLS_SUPPORT)
    mpls_entry_entry_t mpls_entry;
    mpls_entry_entry_t mpls_key;
    int cc_type = 0;
    int mpls_index = 0;
#endif /* BCM_TRIUMPH_SUPPORT &&  BCM_MPLS_SUPPORT */
#if defined(BCM_CMICM_SUPPORT)
    bfd_msg_ctrl_sess_update_t *update_p = NULL;
    mos_msg_data_t uc_msg, uc_reply;
    int encap = 0, update_flags;
#endif /* BCM_CMICM_SUPPORT */

    SET_BFD_INFO;

    CHECK_INIT;

    /* Resolve module and port */
    sal_memset(&l3_egress, 0, sizeof(l3_egress));
    if ( BCM_GPORT_INVALID != endpoint_info->gport) {
        BCM_IF_ERROR_RETURN(_bcm_esw_gport_resolve(unit, endpoint_info->gport,
            &module_id, &port_id, &trunk_id, &local_id));
        if (trunk_id != BCM_TRUNK_INVALID) {
            return BCM_E_PARAM;
        }
    }else if (BCM_SUCCESS(bcm_esw_l3_egress_get(unit, endpoint_info->egress_if, &l3_egress))) {
        module_id = l3_egress.module;
        port_id = l3_egress.port;
    }else{
        return BCM_E_PARAM;
    }

    update = ((endpoint_info->flags & BCM_BFD_ENDPOINT_UPDATE) != 0);

    update_flags = (update) ? 0 : BFD_UPDATE_FLAG_SESSION_CREATE;

    if (endpoint_info->flags & BCM_BFD_ENDPOINT_WITH_ID)
    {
        endpoint_index = endpoint_info->id;

        VALIDATE_ENDPOINT_INDEX(endpoint_index);

        if (update && (!SHR_BITGET(bfd_info_p->endpoints_in_use, endpoint_index)))
        {
            return BCM_E_NOT_FOUND;
        }
        else if (!update && (SHR_BITGET(bfd_info_p->endpoints_in_use, endpoint_index)))
        {
            return BCM_E_EXISTS;
        }

        SET_ENDPOINT(endpoint_index);

        if (update) {
            /*
             * compare new and old BFD parameters
             */
            if (endpoint_p->info.local_discr != endpoint_info->local_discr) {
                update_flags |= BFD_UPDATE_FLAG_LOCAL_DISC;
            }
            if (endpoint_p->info.local_min_tx != endpoint_info->local_min_tx) {
                update_flags |= BFD_UPDATE_FLAG_LOCAL_MIN_TX;
            }
            if (endpoint_p->info.local_min_rx != endpoint_info->local_min_rx) {
                update_flags |= BFD_UPDATE_FLAG_LOCAL_MIN_RX;
            }
            if (endpoint_p->info.local_min_echo != endpoint_info->local_min_echo) {
                update_flags |= BFD_UPDATE_FLAG_LOCAL_MIN_ECHO_RX;
            }
            if (endpoint_p->info.local_diag != endpoint_info->local_diag) {
                update_flags |= BFD_UPDATE_FLAG_LOCAL_DIAG;
            }
            if ( (endpoint_p->info.flags & BCM_BFD_ENDPOINT_DEMAND) != (endpoint_info->flags & BCM_BFD_ENDPOINT_DEMAND)) {
                update_flags |= BFD_UPDATE_FLAG_LOCAL_DEMAND;
            }
            if (endpoint_p->info.local_detect_mult != endpoint_info->local_detect_mult) {
                update_flags |= BFD_UPDATE_FLAG_LOCAL_DETECT_MULT;
            }
            if (endpoint_info->flags & BCM_BFD_ENDPOINT_ENCAP_SET) {
                update_flags |= BFD_UPDATE_FLAG_ENCAP;
            }
            if (endpoint_info->flags & BCM_BFD_ENDPOINT_SHA1_SEQUENCE_INCR) {
                update_flags |= BFD_UPDATE_FLAG_SHA1_XMT_SEQ_INCR;
            }
        }
    }
    else
    {
        if (update)
        {
            /* Replace specified with no ID */
            return BCM_E_PARAM;
        }

        /* Allocate endpoint */
        endpoint_index = _bcm_kt_bfd_find_free_endpoint(bfd_info_p->endpoints_in_use,
                                                    bfd_info_p->endpoint_count, 1, 0);

        if (endpoint_index < 0)
        {
            return BCM_E_FULL;
        }

        endpoint_info->id = endpoint_index;
        SET_ENDPOINT(endpoint_index);
    }

    endpoint_p->endpoint_index = endpoint_index;

    if (BCM_GPORT_IS_LOCAL(endpoint_info->gport) ||
        BCM_GPORT_IS_MODPORT(endpoint_info->gport))
    {
        glp = (module_id << 6) | port_id;
    } else {
        result = _bcm_esw_modid_is_local(unit, module_id, &is_local);
        if (is_local)
        {
            glp = (module_id << 6) | port_id;
        }
    }

    endpoint_p->glp     = glp;
    endpoint_p->info = *endpoint_info; 

#if defined(BCM_CMICM_SUPPORT)
    if ( !update || (endpoint_info->flags & BCM_BFD_ENDPOINT_ENCAP_SET)) {
        result  = _bcm_kt_bfd_encap_set(unit, port_id, endpoint_p);
        if (BCM_FAILURE(result)){
            return result;
        }
        encap = 1;

    }

    /* clear message buffer */
    update_p = &((bfd_msg_ctrl_t*)bfd_info_p->dmabuf_req)->u.su;
    sal_memset(update_p, 0, sizeof(bfd_msg_ctrl_sess_update_t));

    update_p->sess_id = bcm_htonl(endpoint_p->endpoint_index);
    update_p->flags = bcm_htonl(update_flags);
    if (encap) {
        update_p->l2l3l4_offset = bcm_htonl(endpoint_p->l2l3l4_offset);
        update_p->l2l3l4_length = bcm_htonl(endpoint_p->l2l3l4_length);
        update_p->l2l3l4_type = bcm_htonl(endpoint_p->l2l3l4_type);
        if (endpoint_info->mep_id_length) {
            update_p->mep_id_offset = bcm_htonl(endpoint_p->mep_offset);
            update_p->mep_id_length = bcm_htonl(endpoint_info->mep_id_length);
        }
    }
    if (!update) {
        /* session creation time flags only */
        update_p->local_discriminator = bcm_htonl(endpoint_info->local_discr);
        update_p->remote_discriminator = 0;
        update_p->local_min_tx = bcm_htonl(endpoint_info->local_min_tx);
        update_p->local_min_rx = bcm_htonl(endpoint_info->local_min_rx);
        update_p->local_min_echo_rx = bcm_htonl(endpoint_info->local_min_echo);
        update_p->passive = bcm_htonl((endpoint_info->flags & BCM_BFD_ENDPOINT_PASSIVE) ? 1 : 0);
        update_p->local_demand = bcm_htonl((endpoint_info->flags & BCM_BFD_ENDPOINT_DEMAND) ? 1 : 0);
        update_p->local_detect_mult = bcm_htonl(endpoint_info->local_detect_mult);
        update_p->auth_type = bcm_htonl(endpoint_info->auth);
        update_p->auth_key = bcm_htonl(endpoint_info->auth_index);
    }else{
        /* update flags only */
        if (update_flags & BFD_UPDATE_FLAG_LOCAL_DISC) {
            update_p->local_discriminator = bcm_htonl(endpoint_info->local_discr);
        }
        if (update_flags & BFD_UPDATE_FLAG_LOCAL_MIN_TX) {
            update_p->local_min_tx = bcm_htonl(endpoint_info->local_min_tx);
        }
        if (update_flags & BFD_UPDATE_FLAG_LOCAL_MIN_RX) {
            update_p->local_min_rx = bcm_htonl(endpoint_info->local_min_rx);
        }
        if (update_flags & BFD_UPDATE_FLAG_LOCAL_MIN_ECHO_RX) {
            update_p->local_min_echo_rx = bcm_htonl(endpoint_info->local_min_echo);
        }
        if (update_flags & BFD_UPDATE_FLAG_LOCAL_DIAG) {
            update_p->local_diag = bcm_htonl(endpoint_info->local_diag);
        }
        if (update_flags & BFD_UPDATE_FLAG_LOCAL_DEMAND) {
            update_p->local_demand = bcm_htonl((endpoint_info->flags & BCM_BFD_ENDPOINT_DEMAND) ? 1 : 0);
        }
        if (update_flags & BFD_UPDATE_FLAG_LOCAL_DETECT_MULT) {
            update_p->local_detect_mult = bcm_htonl(endpoint_info->local_detect_mult);
        }
        if (update_flags & BFD_UPDATE_FLAG_LOCAL_DETECT_MULT) {
            update_p->local_detect_mult = bcm_htonl(endpoint_info->local_detect_mult);
        }
    }


#endif /* BCM_CMICM_SUPPORT */

    switch(endpoint_info->type) {
    case bcmBFDTunnelTypeUdp:

        /* insert your discriminator field to L2_ENTRY table. */
        sal_memset(&l2_entry, 0, sizeof(l2_entry));

        soc_mem_field32_set(unit, L2Xm, &l2_entry, KEY_TYPEf, 4);
        soc_mem_field32_set(unit, L2Xm, &l2_entry, YOUR_DISCRIMINATORf,
            endpoint_info->local_discr);
        soc_mem_field32_set(unit, L2Xm, &l2_entry, SESSION_IDENTIFIER_TYPEf,
            0);
        soc_mem_field32_set(unit, L2Xm, &l2_entry, VALIDf, 1);

        endpoint_p->info.local_discr = endpoint_info->local_discr;

        if(!is_local){
            soc_mem_field32_set(unit, L2Xm, &l2_entry, BFD_REMOTEf, 1);
            soc_mem_field32_set(unit, L2Xm, &l2_entry, BFD_DST_MODf, module_id);
            soc_mem_field32_set(unit, L2Xm, &l2_entry, BFD_DST_PORTf, port_id);
            soc_mem_field32_set(unit, L2Xm, &l2_entry, BFD_INT_PRIf,
                                                            endpoint_info->int_pri);
        } else {
            soc_mem_field32_set(unit, L2Xm, &l2_entry, BFD_RX_SESSION_INDEXf,
                                endpoint_p->endpoint_index);
            soc_mem_field32_set(unit, L2Xm, &l2_entry, BFD_CPU_QUEUE_CLASSf,
                                endpoint_info->cpu_qid);
        }

        soc_mem_insert(unit, L2Xm, MEM_BLOCK_ANY, &l2_entry);

        /* check whether ipv4 or ipv6 */
        if (endpoint_info->flags & BCM_BFD_ENDPOINT_IPV6){
            v6 = 1;
            if(BCM_IP6_MULTICAST(endpoint_info->src_ip6_addr)) {
                return BCM_E_PARAM;
            }

            sal_memcpy(endpoint_p->info.src_ip6_addr, endpoint_info->src_ip6_addr,
                       sizeof(bcm_ip6_t));
        } else {
            if (BCM_IP4_MULTICAST(endpoint_info->src_ip_addr)) {
                return BCM_E_PARAM;
            }
            endpoint_p->info.src_ip_addr = endpoint_info->src_ip_addr;
        }

        /* L3 entry */
        _bcm_esw_l3_lock(unit);

        if(!v6){
            if (BCM_SUCCESS(_bcm_kt_bfd_find_ipv4_entry(unit,
                                                    endpoint_p->info.vrf_id,
                                                    endpoint_info->src_ip_addr,
                                                    &l3_index, &l3_entry_v4))){
                /* set BFD_ENABLE and LOCAL_ADDRESS */
                result = soc_mem_field32_modify(unit, L3_ENTRY_IPV4_UNICASTm,
                                l3_index, LOCAL_ADDRESSf, 1);
                result |= soc_mem_field32_modify(unit, L3_ENTRY_IPV4_UNICASTm,
                                l3_index, BFD_ENABLEf, 1);

            } else {
                _bcm_esw_l3_unlock(unit);
                return BCM_E_PARAM;
            }
        }
        else {
            if (BCM_SUCCESS(_bcm_kt_bfd_find_ipv6_entry(unit,
                                                    endpoint_p->info.vrf_id,
                                                    endpoint_info->src_ip6_addr,
                                                    &l3_index, &l3_entry_v6))){
                /* set BFD_ENABLE and LOCAL_ADDRESS */
                result = soc_mem_field32_modify(unit, L3_ENTRY_IPV6_UNICASTm,
                                l3_index, LOCAL_ADDRESSf, 1);
                result |= soc_mem_field32_modify(unit, L3_ENTRY_IPV6_UNICASTm,
                                l3_index, BFD_ENABLEf, 1);

            }
            else {
                _bcm_esw_l3_unlock(unit);
                return BCM_E_PARAM;

            }

        }

        _bcm_esw_l3_unlock(unit);
        break;
    case bcmBFDTunnelTypeIp4in4:
    case bcmBFDTunnelTypeIp6in4:
    case bcmBFDTunnelTypeIp4in6:
    case bcmBFDTunnelTypeIp6in6:
        break;
    case bcmBFDTunnelTypeGRE:
        break;
    case bcmBFDTunnelTypeMpls:
    case bcmBFDTunnelTypeMplsTpCc:
    case bcmBFDTunnelTypeMplsTpCcCv:
    case bcmBFDTunnelTypePweControlWord:
    case bcmBFDTunnelTypePweRouterAlert:
    case bcmBFDTunnelTypePweTtl:
#if defined(BCM_TRIUMPH_SUPPORT) && defined(BCM_MPLS_SUPPORT)

        /* insert mpls label field to L2_ENTRY table. */
        sal_memset(&l2_entry, 0, sizeof(l2_entry));

        soc_mem_field32_set(unit, L2Xm, &l2_entry, KEY_TYPEf, 4);
        soc_mem_field32_set(unit, L2Xm, &l2_entry, SESSION_IDENTIFIER_TYPEf, 1);
        soc_mem_field32_set(unit, L2Xm, &l2_entry, LABELf,
            endpoint_info->label);
        soc_mem_field32_set(unit, L2Xm, &l2_entry, VALIDf, 1);

        endpoint_p->info.label = endpoint_info->label;

        if(!is_local){
            /* set BFD_REMOTE = 1, DST_MOD, DST_PORT */
            soc_mem_field32_set(unit, L2Xm, &l2_entry, BFD_REMOTEf, 1);
            soc_mem_field32_set(unit, L2Xm, &l2_entry, BFD_DST_MODf, module_id);
            soc_mem_field32_set(unit, L2Xm, &l2_entry, BFD_DST_PORTf, port_id);
            soc_mem_field32_set(unit, L2Xm, &l2_entry, BFD_INT_PRIf,
                                                       endpoint_info->int_pri);
        } else {
            soc_mem_field32_set(unit, L2Xm, &l2_entry, BFD_RX_SESSION_INDEXf,
                                endpoint_p->endpoint_index);
            soc_mem_field32_set(unit, L2Xm, &l2_entry, BFD_CPU_QUEUE_CLASSf,
                                endpoint_info->cpu_qid);
        }

        soc_mem_insert(unit, L2Xm, MEM_BLOCK_ANY, &l2_entry);

        SOC_IF_ERROR_RETURN(bcm_tr_mpls_lock (unit));

        sal_memset(&mpls_key, 0, sizeof(mpls_key));
        soc_MPLS_ENTRYm_field32_set(unit, &mpls_key, MPLS_LABELf, endpoint_info->label);

        SOC_IF_ERROR_RETURN(soc_mem_search(unit, MPLS_ENTRYm,
                                           MEM_BLOCK_ANY, &mpls_index,
                                           &mpls_key, &mpls_entry, 0));

        if ((soc_MPLS_ENTRYm_field32_get(unit,
               &mpls_entry, VALIDf) != 0x1)) {

            bcm_tr_mpls_unlock (unit);
            return BCM_E_PARAM;
        }

        soc_MPLS_ENTRYm_field32_set(unit, &mpls_entry, SESSION_IDENTIFIER_TYPEf, 1);

        soc_MPLS_ENTRYm_field32_set(unit, &mpls_entry, BFD_ENABLEf, 1);


        if (endpoint_info->type == bcmBFDTunnelTypePweControlWord){
            cc_type = 1;
        } else if (endpoint_info->type == bcmBFDTunnelTypePweRouterAlert){
            cc_type = 2;
        } else if (endpoint_info->type == bcmBFDTunnelTypePweTtl){
            cc_type = 3;
        }

        soc_MPLS_ENTRYm_field32_set(unit, &mpls_entry, PW_CC_TYPEf, cc_type);

        SOC_IF_ERROR_RETURN(soc_mem_write(unit, MPLS_ENTRYm,
                                        MEM_BLOCK_ANY, mpls_index, &mpls_entry));
        bcm_tr_mpls_unlock (unit);
#endif /* BCM_TRIUMPH_SUPPORT &&  BCM_MPLS_SUPPORT */
        break;

    default:
        break;
    }

    SOC_IF_ERROR_RETURN(result);

    soc_cm_sflush(unit, update_p, sizeof(bfd_msg_ctrl_sess_update_t));

    sal_memset(&uc_msg, 0, sizeof(uc_msg));
    uc_msg.s.mclass = MOS_MSG_CLASS_BFD;
    uc_msg.s.subclass = MOS_MSG_SUBCLASS_BFD_SESS_SET;
    uc_msg.s.len = bcm_htons(sizeof(bfd_msg_ctrl_sess_update_t));
    uc_msg.s.data = bcm_htonl(soc_cm_l2p(unit, update_p));

    sal_memset(&uc_reply, 0, sizeof(uc_reply));

#ifdef BFD_UC_MESSAGE_ON
    result = soc_cmic_uc_msg_send_receive(unit, bfd_info_p->bte_num, &uc_msg, &uc_reply, _BFD_UC_MSG_TIMEOUT_USECS);
    if ( (SOC_E_NONE == result) &&
         (uc_reply.s.subclass == MOS_MSG_SUBCLASS_BFD_SESS_SET_REPLY) &&
         (uc_reply.s.mclass == MOS_MSG_CLASS_BFD) ) {
        /* success */
    }else{
        result = BCM_E_INTERNAL;
    }
#endif

    /* mark endpoint in use */
    SHR_BITSET(bfd_info_p->endpoints_in_use, endpoint_p->endpoint_index);

    return BCM_E_NONE;
}


/*
 * Function:
 *      bcm_kt_bfd_endpoint_get
 * Purpose:
 *      Get an BFD endpoint object
 * Parameters:
 *      unit - (IN) Unit number.
 *      endpoint - (IN) The ID of the endpoint object to get
 *      endpoint_info - (OUT) Pointer to an BFD endpoint structure to receive the data
 * Returns:
 *      BCM_E_NONE Operation completed successfully
 *      BCM_E_NOT_FOUND Endpoint does not exist
 *      BCM_E_PARAM Invalid parameter specified
 *      BCM_E_INTERNAL Failed to read endpoint information
 * Notes:
 */
int
bcm_kt_bfd_endpoint_get(
    int unit,
    bcm_bfd_endpoint_t endpoint,
    bcm_bfd_endpoint_info_t *endpoint_info)
{
    _bcm_bfd_info_t *bfd_info_p;
    _bcm_bfd_endpoint_t *endpoint_p;

    SET_BFD_INFO;

    CHECK_INIT;

    VALIDATE_ENDPOINT_INDEX(endpoint);

    SET_ENDPOINT(endpoint);

    if (!SHR_BITGET(bfd_info_p->endpoints_in_use, endpoint))
    {
        return BCM_E_NOT_FOUND;
    }

    return _bcm_kt_bfd_get_endpoint(unit, endpoint, endpoint_p,
                                    endpoint_info);
}


/*
 * Function:
 *      bcm_kt_bfd_endpoint_destroy
 * Purpose:
 *      Destroy an BFD endpoint object
 * Parameters:
 *      unit - (IN) Unit number.
 *      endpoint - (IN) The ID of the BFD endpoint object to destroy
 * Returns:
 *      BCM_E_NONE Operation completed successfully
 *      BCM_E_NOT_FOUND Attempt to destroy endpoint which does not exist
 *      BCM_E_INTERNAL Unable to release resource /
 *                     Failed to read memory or read or write register
 * Notes:
 */
int
bcm_kt_bfd_endpoint_destroy(
    int unit,
    bcm_bfd_endpoint_t endpoint)
{
    _bcm_bfd_info_t *bfd_info_p;
    _bcm_bfd_endpoint_t *endpoint_p;

    SET_BFD_INFO;

    CHECK_INIT;

    VALIDATE_ENDPOINT_INDEX(endpoint);

    SET_ENDPOINT(endpoint);

    if (!SHR_BITGET(bfd_info_p->endpoints_in_use, endpoint))
    {
        return BCM_E_NOT_FOUND;
    }

    return _bcm_kt_bfd_destroy_endpoint(unit, endpoint_p);
}

/*
 * Function:
 *      bcm_kt_bfd_endpoint_destroy_all
 * Purpose:
 *      Destroy all BFD endpoint objects
 * Parameters:
 *      unit - (IN) Unit number.
 * Returns:
 *      BCM_E_NONE Operation completed successfully
 *      BCM_E_INTERNAL Unable to release resource /
 *                     Failed to read memory or read or write register
 * Notes:
 */
int
bcm_kt_bfd_endpoint_destroy_all(
    int unit)
{
    _bcm_bfd_info_t *bfd_info_p;
    _bcm_bfd_endpoint_t *endpoint_p;
    int e;
    int result;


    SET_BFD_INFO;

    CHECK_INIT;

    for (e=0; e<bfd_info_p->endpoint_count; e++) {
        if (SHR_BITGET(bfd_info_p->endpoints_in_use, e)) {
            SET_ENDPOINT(e);
            result = _bcm_kt_bfd_destroy_endpoint(unit, endpoint_p);
            if (BCM_FAILURE(result)) {
                return result;
            }
        }
    }

    return BCM_E_NONE;
}

/*
 * Function:
 *      bcm_kt_bfd_endpoint_poll
 * Purpose:
 *      Poll an BFD endpoint object.  Valid only for endpoint in Demand
 *      Mode and in state bcmBFDStateUp.
 * Parameters:
 *      unit - (IN) Unit number.
 *      endpoint - (IN) The ID of the BFD endpoint object to poll
 * Returns:
 *      BCM_E_NONE Operation completed successfully
 *      BCM_E_NOT_FOUND BFD Session ID not in use
 *      BCM_E_INTERNAL Unable to read or write resource
 *      BCM_E_PARAM Session not in Demand Mode/
 *                 Session not in UP state
 * Notes: The poll operation will be initiated.  Poll failure will be
 * signalled via asynchronous callback (aka session failure)
 */
int
bcm_kt_bfd_endpoint_poll(
    int unit,
    bcm_bfd_endpoint_t endpoint)
{
    _bcm_bfd_info_t *bfd_info_p;
    _bcm_bfd_endpoint_t *endpoint_p;
#if defined(BCM_CMICM_SUPPORT)
    mos_msg_data_t uc_msg, uc_reply;
#endif
    int result = BCM_E_NONE;

    SET_BFD_INFO;

    CHECK_INIT;

    VALIDATE_ENDPOINT_INDEX(endpoint);

    SET_ENDPOINT(endpoint);

    if (!SHR_BITGET(bfd_info_p->endpoints_in_use, endpoint))
    {
        return BCM_E_NOT_FOUND;
    }

    /* Issue poll request on session not in the UP state will fail */
    sal_memset(&uc_msg, 0, sizeof(uc_msg));
    sal_memset(&uc_reply, 0, sizeof(uc_reply));
    uc_msg.s.mclass = MOS_MSG_CLASS_BFD;
    uc_msg.s.subclass = MOS_MSG_SUBCLASS_BFD_SESS_POLL;
    uc_msg.s.len = bcm_htons(endpoint_p->endpoint_index);
    uc_msg.s.data = 0;

#ifdef BFD_UC_MESSAGE_ON
    result = soc_cmic_uc_msg_send_receive(unit, bfd_info_p->bte_num, &uc_msg, &uc_reply, _BFD_UC_MSG_TIMEOUT_USECS);
    if ( (SOC_E_NONE == result) &&
         (uc_reply.s.subclass == MOS_MSG_SUBCLASS_BFD_SESS_DELETE_REPLY) &&
         (uc_reply.s.mclass == MOS_MSG_CLASS_BFD) ) {
        /* success */
    }else{
        result = BCM_E_INTERNAL;
    }
#endif

    return result;

}

/*
 * Function:
 *      bcm_kt_bfd_event_register
 * Purpose:
 *      Register a callback for handling BFD events
 * Parameters:
 *      unit - (IN) Unit number.
 *      event_types - (IN) The set of BFD events for which the specified
 *                    callback will be invoked
 *      cb - (IN) A pointer to the callback function
 *      user_data - (IN) Pointer to user data to pass to the callback
 * Returns:
 *      BCM_E_NONE Operation completed successfully
 * Notes:
 */
int
bcm_kt_bfd_event_register(
    int unit,
    bcm_bfd_event_types_t event_types,
    bcm_bfd_event_cb cb,
    void *user_data)
{
    _bcm_bfd_info_t *bfd_info_p;
    uint32 result;
    _bcm_bfd_event_handler_t *event_handler_p;
    _bcm_bfd_event_handler_t *previous_p = NULL;
    bcm_bfd_event_type_t event_type;

    SET_BFD_INFO;

    CHECK_INIT;

    if (cb == NULL)
    {
        return BCM_E_PARAM;
    }

    SHR_BITTEST_RANGE(event_types.w, 0, bcmBFDEventCount, result);

    if (result == 0)
    {
        return BCM_E_PARAM;
    }

    for (event_handler_p = bfd_info_p->event_handler_list_p;
        event_handler_p != NULL;
        event_handler_p = event_handler_p->next_p)
    {
        if (event_handler_p->cb == cb)
        {
            break;
        }

        previous_p = event_handler_p;
    }

    if (event_handler_p == NULL)
    {
        /* This handler hasn't been registered yet */

        event_handler_p = sal_alloc(sizeof(_bcm_bfd_event_handler_t),
            "BFD event handler");

        if (event_handler_p == NULL)
        {
            return BCM_E_MEMORY;
        }

        event_handler_p->next_p = NULL;
        event_handler_p->cb = cb;

        SHR_BITCLR_RANGE(event_handler_p->event_types.w, 0, bcmBFDEventCount);

        if (previous_p != NULL)
        {
            previous_p->next_p = event_handler_p;
        }
        else
        {
            bfd_info_p->event_handler_list_p = event_handler_p;
        }
    }

    for (event_type = 0; event_type < bcmBFDEventCount; ++event_type)
    {
        if (SHR_BITGET(event_types.w, event_type))
        {
            if (!SHR_BITGET(event_handler_p->event_types.w, event_type))
            {
                /* This handler isn't handling this event yet */
                SHR_BITSET(event_handler_p->event_types.w, event_type);
            }
        }
    }

    event_handler_p->user_data = user_data;

    return BCM_E_NONE;
}

/*
 * Function:
 *      bcm_kt_bfd_event_unregister
 * Purpose:
 *      Unregister a callback for handling BFD events
 * Parameters:
 *      unit - (IN) Unit number.
 *      event_types - (IN) The set of BFD events for which the specified
 *                    callback should not be invoked
 *      cb - (IN) A pointer to the callback function
 * Returns:
 *      BCM_E_NONE Operation completed successfully
 * Notes:
 */
int
bcm_kt_bfd_event_unregister(
    int unit,
    bcm_bfd_event_types_t event_types,
    bcm_bfd_event_cb cb)
{
    _bcm_bfd_info_t *bfd_info_p;
    uint32 result;
    _bcm_bfd_event_handler_t *event_handler_p;
    _bcm_bfd_event_handler_t *previous_p = NULL;
    bcm_bfd_event_type_t event_type;

    SET_BFD_INFO;

    CHECK_INIT;

    if (cb == NULL)
    {
        return BCM_E_PARAM;
    }

    SHR_BITTEST_RANGE(event_types.w, 0, bcmBFDEventCount, result);

    if (result == 0)
    {
        return BCM_E_PARAM;
    }

    for (event_handler_p = bfd_info_p->event_handler_list_p;
        event_handler_p != NULL;
        event_handler_p = event_handler_p->next_p)
    {
        if (event_handler_p->cb == cb)
        {
            break;
        }

        previous_p = event_handler_p;
    }

    if (event_handler_p == NULL)
    {
        return BCM_E_NOT_FOUND;
    }

    for (event_type = 0; event_type < bcmBFDEventCount; ++event_type)
    {
        if (SHR_BITGET(event_types.w, event_type))
        {
            SHR_BITCLR(event_handler_p->event_types.w, event_type);
        }
    }

    SHR_BITTEST_RANGE(event_handler_p->event_types.w, 0, bcmBFDEventCount,
        result);

    if (result == 0)
    {
        /* No more events for this handler to handle */
        if (previous_p != NULL)
        {
            previous_p->next_p = event_handler_p->next_p;
        }
        else
        {
            bfd_info_p->event_handler_list_p = event_handler_p->next_p;
        }

        sal_free(event_handler_p);
    }

    return BCM_E_NONE;
}

/*
 * Function:
 *      bcm_kt_bfd_endpoint_stat_get
 * Purpose:
 *      Get BFD endpoint stats
 * Parameters:
 *      unit - (IN) Unit number.
 *      endpoint - (IN) The ID of the endpoint object to get stats for
 *      ctr_info - (IN/OUT) Pointer to endpoint count structure to receive the data
 *      clear - (IN) If set, clear stats after read
 * Returns:
 *      BCM_E_NONE Operation completed successfully
 *      BCM_E_PARAM Null pointer to counter information structure
 *      BCM_E_MEMORY Unable to allocate memory
 *      BCM_E_INTERNAL Unable to obtain/release resource lock /
 *                     Failed to read or write register
 * Notes:
 */
int
bcm_kt_bfd_endpoint_stat_get(
    int unit,
    bcm_bfd_endpoint_t endpoint,
    bcm_bfd_endpoint_stat_t *ctr_info,
    uint8 clear)
{
    int result = BCM_E_NONE;
    _bcm_bfd_info_t *bfd_info_p;
    mos_msg_data_t uc_msg, uc_reply;
    bfd_msg_ctrl_stat_req_t *ctr_req;
    bfd_msg_ctrl_stat_reply_t *ctr_reply;

    if (!ctr_info) {
        return BCM_E_PARAM;
    }

    SET_BFD_INFO;

    CHECK_INIT;

    ctr_reply = &((bfd_msg_ctrl_t*)bfd_info_p->dmabuf_reply)->u.cnt_reply;

    ctr_req = &((bfd_msg_ctrl_t*)bfd_info_p->dmabuf_req)->u.cnt_req;
    sal_memset(ctr_req, 0, sizeof(bfd_msg_ctrl_stat_req_t));
    ctr_req->sess_id = endpoint;
    ctr_req->clear = clear;
    ctr_req->addr = soc_cm_l2p(unit, ctr_reply);
    soc_cm_sflush(unit, ctr_req, sizeof(bfd_msg_ctrl_stat_req_t));

    sal_memset(&uc_msg, 0, sizeof(uc_msg));
    uc_msg.s.mclass = MOS_MSG_CLASS_BFD;
    uc_msg.s.subclass = MOS_MSG_SUBCLASS_BFD_COUNTS_GET;
    uc_msg.s.len = bcm_htons(sizeof(bfd_msg_ctrl_stat_req_t));
    uc_msg.s.data = bcm_htonl(soc_cm_l2p(unit, ctr_req));

    sal_memset(&uc_reply, 0, sizeof(uc_reply));

#ifdef BFD_UC_MESSAGE_ON
    result = soc_cmic_uc_msg_send_receive(unit, bfd_info_p->bte_num, &uc_msg, &uc_reply, _BFD_UC_MSG_TIMEOUT_USECS);
    if ( (SOC_E_NONE == result) &&
         (uc_reply.s.subclass == MOS_MSG_SUBCLASS_BFD_COUNTS_GET_REPLY) &&
         (uc_reply.s.mclass == MOS_MSG_CLASS_BFD) ) {
        ctr_info->packets_in = ctr_reply->packets_in;
        ctr_info->packets_out = ctr_reply->packets_out;
        ctr_info->packets_drop = ctr_reply->packets_drop;
    }else{
        result = BCM_E_INTERNAL;
    }
#endif

    return result;
}
/*
 * Function:
 *      bcm_kt_bfd_auth_sha1_set
 * Purpose:
 *      Set SHA1 authentication entry
 * Parameters:
 *      unit - (IN) Unit number.
 *      index - (IN) Index of the SHA1 entry to configure
 *      sha1 - (IN) Pointer to SHA1 info structure
 * Returns:
 *      BCM_E_NONE Operation completed successfully
 *      BCM_E_PARAM Null pointer to SHA1 information structure
 *      BCM_E_MEMORY Unable to allocate memory
 *      BCM_E_INTERNAL Unable to obtain/release resource lock /
 *                     Failed to read or write register
 * Notes:
 */
int
bcm_kt_bfd_auth_sha1_set(
    int unit,
    int index,
    bcm_bfd_auth_sha1_t *sha1)
{
    int result = BCM_E_NONE;
    _bcm_bfd_info_t *bfd_info_p;
    mos_msg_data_t uc_msg, uc_reply;
    bfd_msg_ctrl_auth_sha1_t *auth_p;

    SET_BFD_INFO;

    CHECK_INIT;

    if ( (!sha1) ||
         (index >= bfd_info_p->num_auth_sha1_key) ||
         (index < 0) )   {
        return BCM_E_PARAM;
    }

    auth_p = &((bfd_msg_ctrl_t*)bfd_info_p->dmabuf_req)->u.sha1;
    sal_memset(auth_p, 0, sizeof(bfd_msg_ctrl_auth_sha1_t));
    auth_p->index = index;
    auth_p->enable = sha1->enable;
    auth_p->sequence = sha1->sequence;
    sal_memcpy(&auth_p->key, &sha1->key, BCM_BFD_AUTH_SHA1_KEY_LENGTH);
    soc_cm_sflush(unit, auth_p, sizeof(bfd_msg_ctrl_auth_sha1_t));

    sal_memset(&uc_msg, 0, sizeof(uc_msg));
    sal_memset(&uc_reply, 0, sizeof(uc_reply));
    uc_msg.s.mclass = MOS_MSG_CLASS_BFD;
    uc_msg.s.subclass = MOS_MSG_SUBCLASS_BFD_AUTH_SHA1_SET;
    uc_msg.s.len = bcm_htons(sizeof(bfd_msg_ctrl_t));
    uc_msg.s.data = bcm_htonl(soc_cm_l2p(unit, auth_p));

#ifdef BFD_UC_MESSAGE_ON
    result = soc_cmic_uc_msg_send_receive(unit, bfd_info_p->bte_num, &uc_msg, &uc_reply, _BFD_UC_MSG_TIMEOUT_USECS);
    if ((SOC_E_NONE == result) &&
        (uc_reply.s.mclass == MOS_MSG_CLASS_BFD) &&
        (uc_reply.s.subclass == MOS_MSG_SUBCLASS_BFD_AUTH_SHA1_SET_REPLY) &&
        (uc_reply.s.len == 0)) {
        result = BCM_E_NONE;
    } else {
        result = BCM_E_INTERNAL;
    }
#endif

    if (BCM_SUCCESS(result)) {
        bfd_info_p->auth_sha1[index] = *sha1;
    }

    return result;
}


/*
 * Function:
 *      bcm_kt_bfd_auth_sha1_get
 * Purpose:
 *      Get SHA1 authentication entry
 * Parameters:
 *      unit - (IN) Unit number.
 *      index - (IN) Index of the SHA1 entry to retrieve
 *      sha1 - (IN/OUT) Pointer to SHA1 info structure to receive the data
 * Returns:
 *      BCM_E_NONE Operation completed successfully
 *      BCM_E_PARAM Null pointer to SHA1 information structure
 *      BCM_E_MEMORY Unable to allocate memory
 *      BCM_E_INTERNAL Unable to obtain/release resource lock /
 *                     Failed to read or write register
 * Notes:
 */
int
bcm_kt_bfd_auth_sha1_get(
    int unit,
    int index,
    bcm_bfd_auth_sha1_t *sha1)
{
    int result = BCM_E_NONE;
    _bcm_bfd_info_t *bfd_info_p;
    bfd_msg_ctrl_auth_sha1_t *auth_p;
    mos_msg_data_t uc_msg, uc_reply;

    SET_BFD_INFO;

    CHECK_INIT;

    if ( (!sha1) ||
         (index >= bfd_info_p->num_auth_sha1_key) ||
         (index < 0) )   {
        return BCM_E_PARAM;
    }

    /* if warmboot, get authentication data from BTE, else use cache */
    if (SOC_WARM_BOOT(unit))
    {
        auth_p = &((bfd_msg_ctrl_t*)bfd_info_p->dmabuf_reply)->u.sha1;
        sal_memset(auth_p, 0, sizeof(bfd_msg_ctrl_auth_sha1_t));

        sal_memset(&uc_msg, 0, sizeof(uc_msg));
        sal_memset(&uc_reply, 0, sizeof(uc_reply));
        uc_msg.s.mclass = MOS_MSG_CLASS_BFD;
        uc_msg.s.subclass = MOS_MSG_SUBCLASS_BFD_AUTH_SHA1_GET;
        uc_msg.s.len = bcm_htons(index);
        uc_msg.s.data = bcm_htonl(soc_cm_l2p(unit, auth_p));

#ifdef BFD_UC_MESSAGE_ON
        result = soc_cmic_uc_msg_send_receive(unit, bfd_info_p->bte_num, &uc_msg, &uc_reply, _BFD_UC_MSG_TIMEOUT_USECS);
        if ((SOC_E_NONE == result) &&
            (uc_reply.s.mclass == MOS_MSG_CLASS_BFD) &&
            (uc_reply.s.subclass == MOS_MSG_SUBCLASS_BFD_AUTH_SHA1_GET_REPLY) &&
            (uc_reply.s.len == 0)) {
            sha1->enable = (auth_p->index) ? 1 : 0;
            sha1->sequence = auth_p->sequence;
            sal_memcpy(sha1->key, auth_p->key, BCM_BFD_AUTH_SHA1_KEY_LENGTH);
            result = BCM_E_NONE;
        } else {
            result = BCM_E_INTERNAL;
        }
#endif
    } else {
        *sha1 = bfd_info_p->auth_sha1[index];
    }

    return result;
}

/*
 * Function:
 *      bcm_kt_bfd_auth_simple_password_set
 * Purpose:
 *      Set Simple Password authentication entry
 * Parameters:
 *      unit - (IN) Unit number.
 *      index - (IN) Index of the Simple Password entry to configure
 *      sp - (IN) Pointer to Simple Password info structure
 * Returns:
 *      BCM_E_NONE Operation completed successfully
 *      BCM_E_PARAM Null pointer to SHA1 information structure
 *      BCM_E_MEMORY Unable to allocate memory
 *      BCM_E_INTERNAL Unable to obtain/release resource lock /
 *                     Failed to read or write register
 * Notes:
 */
int
bcm_kt_bfd_auth_simple_password_set(
    int unit,
    int index,
    bcm_bfd_auth_simple_password_t *sp)
{
    int result = BCM_E_NONE;
    _bcm_bfd_info_t *bfd_info_p;
    mos_msg_data_t uc_msg, uc_reply;
    bfd_msg_ctrl_auth_sp_t *auth_p;

    SET_BFD_INFO;

    CHECK_INIT;

    if ( (!sp) ||
         (index >= bfd_info_p->num_auth_sp_key) ||
         (index < 0) )   {
        return BCM_E_PARAM;
    }

    auth_p = &((bfd_msg_ctrl_t*)bfd_info_p->dmabuf_req)->u.sp;
    sal_memset(auth_p, 0, sizeof(bfd_msg_ctrl_auth_sp_t));
    auth_p->index = index;
    auth_p->length = sp->length;
    sal_memcpy(&auth_p->password, &sp->password, BFD_AUTH_PASSWORD_LENGTH_MAX);
    soc_cm_sflush(unit, auth_p, sizeof(bfd_msg_ctrl_auth_sp_t));

    sal_memset(&uc_msg, 0, sizeof(uc_msg));
    sal_memset(&uc_reply, 0, sizeof(uc_reply));
    uc_msg.s.mclass = MOS_MSG_CLASS_BFD;
    uc_msg.s.subclass = MOS_MSG_SUBCLASS_BFD_AUTH_SP_SET;
    uc_msg.s.len = bcm_htons(sizeof(bfd_msg_ctrl_auth_sp_t));
    uc_msg.s.data = bcm_htonl(soc_cm_l2p(unit, auth_p));

#ifdef BFD_UC_MESSAGE_ON
    result = soc_cmic_uc_msg_send_receive(unit, bfd_info_p->bte_num, &uc_msg, &uc_reply, _BFD_UC_MSG_TIMEOUT_USECS);
    if ((SOC_E_NONE == result) &&
        (uc_reply.s.mclass == MOS_MSG_CLASS_BFD) &&
        (uc_reply.s.subclass == MOS_MSG_SUBCLASS_BFD_AUTH_SP_SET_REPLY) &&
        (uc_reply.s.len == 0)) {
        result = BCM_E_NONE;
    } else {
        result = BCM_E_INTERNAL;
    }
#endif

    if (BCM_SUCCESS(result)) {
        bfd_info_p->auth_sp[index] = *sp;
    }

    return result;
}

/*
 * Function:
 *      bcm_kt_bfd_auth_simple_password_get
 * Purpose:
 *      Get Simple Password authentication entry
 * Parameters:
 *      unit - (IN) Unit number.
 *      index - (IN) Index of the Simple Password entry to retrieve
 *      sp - (IN/OUT) Pointer to Simple Password info structure to receive the data
 * Returns:
 *      BCM_E_NONE Operation completed successfully
 *      BCM_E_PARAM Null pointer to SHA1 information structure
 *      BCM_E_MEMORY Unable to allocate memory
 *      BCM_E_INTERNAL Unable to obtain/release resource lock /
 *                     Failed to read or write register
 * Notes:
 */
int
bcm_kt_bfd_auth_simple_password_get(
    int unit,
    int index,
    bcm_bfd_auth_simple_password_t *sp)
{
    int result = BCM_E_NONE;
    _bcm_bfd_info_t *bfd_info_p;
    bfd_msg_ctrl_auth_sp_t *auth_p;
    mos_msg_data_t uc_msg, uc_reply;

    SET_BFD_INFO;

    CHECK_INIT;

    if ( (!sp) ||
         (index >= bfd_info_p->num_auth_sp_key) ||
         (index < 0) )   {
        return BCM_E_PARAM;
    }

    /* if warmboot, get authentication data from BTE, else use cache */
    if (SOC_WARM_BOOT(unit))
    {
        auth_p = &((bfd_msg_ctrl_t*)bfd_info_p->dmabuf_reply)->u.sp;
        sal_memset(auth_p, 0, sizeof(bfd_msg_ctrl_auth_sp_t));
        soc_cm_sflush(unit, auth_p, sizeof(bfd_msg_ctrl_auth_sp_t));

        sal_memset(&uc_msg, 0, sizeof(uc_msg));
        sal_memset(&uc_reply, 0, sizeof(uc_reply));
        uc_msg.s.mclass = MOS_MSG_CLASS_BFD;
        uc_msg.s.subclass = MOS_MSG_SUBCLASS_BFD_AUTH_SP_GET;
        uc_msg.s.len = bcm_htons(index);
        uc_msg.s.data = bcm_htonl(soc_cm_l2p(unit, auth_p));

#ifdef BFD_UC_MESSAGE_ON
        result = soc_cmic_uc_msg_send_receive(unit, bfd_info_p->bte_num, &uc_msg, &uc_reply, _BFD_UC_MSG_TIMEOUT_USECS);
        if ((SOC_E_NONE == result) &&
            (uc_reply.s.mclass == MOS_MSG_CLASS_BFD) &&
            (uc_reply.s.subclass == MOS_MSG_SUBCLASS_BFD_AUTH_SP_GET_REPLY) &&
            (uc_reply.s.len == 0)) {
            sp->length= (auth_p->length) ? 1 : 0;
            sal_memcpy(sp->password, auth_p->password,
                       BFD_AUTH_PASSWORD_LENGTH_MAX);
            result = BCM_E_NONE;
        } else {
            result = BCM_E_INTERNAL;
        }
#endif
    } else {
        *sp = bfd_info_p->auth_sp[index];
    }

    return result;
}

#if defined(BCM_WARM_BOOT_SUPPORT)
int _bcm_kt_bfd_sync(int unit)
{

    return BCM_E_NONE;
}
#endif /* BCM_WARM_BOOT_SUPPORT */

#ifdef BCM_WARM_BOOT_SUPPORT_SW_DUMP
/*
 * Function:
 *     _bcm_bfd_sw_dump
 * Purpose:
 *     Displays BFD information maintained by software.
 * Parameters:
 *     unit - Device unit number
 * Returns:
 *     None
 */
void
_bcm_kt_bfd_sw_dump(int unit)
{
    _bcm_bfd_info_t *bfd_info_p;
    _bcm_bfd_endpoint_t *endpoint_p;
    int endpoint_idx;
    SHR_BITDCL word;

    SET_BFD_INFO;

    soc_cm_print("\nSW Information BFD - Unit %d\n", unit);

    soc_cm_print("\n endpoints_in_use \n");
    for (word = 0; word < _SHR_BITDCLSIZE
         (bfd_info_p->endpoint_count); word++) {
        soc_cm_print(" word %d value %x ", word,
                     bfd_info_p->endpoints_in_use[word]);
    }

    soc_cm_print("\n Endpoint Information \n");
    for (endpoint_idx = 0; endpoint_idx < bfd_info_p->endpoint_count;
         endpoint_idx++) {
        if (!SHR_BITGET(bfd_info_p->endpoints_in_use, endpoint_idx)) {
            continue;
        }
        SET_ENDPOINT(endpoint_idx);
        soc_cm_print("\n Endpoint index %d\n", endpoint_idx);
        soc_cm_print("\t Type %x\n", endpoint_p->info.type);
        soc_cm_print("\t GLP %x\n", endpoint_p->glp);
        soc_cm_print("\t VRF id %x\n", endpoint_p->info.vrf_id);

    }
    return;
}
#endif
#endif /* defined(BCM_KATANA) */
#endif /* INCLUDE_L3 */
